python 59 lines · 10 steps

Building a paginated JSON API with Flask MethodView

A class-based Flask view exposes a collection endpoint that lists articles with pagination links and creates new ones.

Explained by highlit
1from flask import Blueprint, request, url_for, jsonify, abort
2from flask.views import MethodView
3 
4from .models import Article, db
5from .schemas import ArticleSchema
6 
7articles_bp = Blueprint("articles", __name__, url_prefix="/api/articles")
8article_schema = ArticleSchema()
9 
10 
11class ArticleListAPI(MethodView):
12 init_every_request = False
13 
14 def get(self):
15 page = request.args.get("page", 1, type=int)
16 per_page = min(request.args.get("per_page", 20, type=int), 100)
17 
18 query = Article.query.order_by(Article.published_at.desc())
19 if author := request.args.get("author"):
20 query = query.filter_by(author=author)
21 
22 pagination = query.paginate(page=page, per_page=per_page, error_out=False)
23 
24 def page_url(p):
25 return url_for("articles.list", page=p, per_page=per_page, _external=True)
26 
27 return jsonify({
28 "data": article_schema.dump(pagination.items, many=True),
29 "meta": {
30 "page": pagination.page,
31 "per_page": pagination.per_page,
32 "total": pagination.total,
33 "pages": pagination.pages,
34 },
35 "links": {
36 "self": page_url(page),
37 "next": page_url(pagination.next_num) if pagination.has_next else None,
38 "prev": page_url(pagination.prev_num) if pagination.has_prev else None,
39 },
40 })
41 
42 def post(self):
43 errors = article_schema.validate(request.get_json() or {})
44 if errors:
45 abort(422, description=errors)
46 
47 article = article_schema.load(request.get_json())
48 db.session.add(article)
49 db.session.commit()
50 
51 response = jsonify(article_schema.dump(article))
52 response.status_code = 201
53 response.headers["Location"] = url_for(
54 "articles.detail", article_id=article.id, _external=True
55 )
56 return response
57 
58 
59articles_bp.add_url_rule("/", view_func=ArticleListAPI.as_view("list"))
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Class-based MethodView maps HTTP verbs to methods, keeping list and create logic on one endpoint.
  2. 2Building hypermedia links from url_for keeps pagination self-describing and decoupled from URL structure.
  3. 3Validate before load and return 201 with a Location header to follow REST conventions for resource creation.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a paginated JSON API with Flask MethodView — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code