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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Class-based MethodView maps HTTP verbs to methods, keeping list and create logic on one endpoint.
- 2Building hypermedia links from url_for keeps pagination self-describing and decoupled from URL structure.
- 3Validate before load and return 201 with a Location header to follow REST conventions for resource creation.
Related explainers
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
rust
use axum::{ extract::{Path, State}, response::sse::{Event, KeepAlive, Sse}, };
Streaming import progress with SSE in Axum
server-sent-events
streams
watch-channel
Advanced
7 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
python
import re from functools import total_ordering from typing import Optional
Parsing and comparing semantic versions
regex
operator-overloading
sorting
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/building-a-paginated-json-api-with-flask-methodview-explained-python-6859/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.