Easy API Pagination in Flask
In this post I will show you how to set up API pagination in Flask, thanks primarily to Flask-SqlAlchemy’s paginate method.
Our goal is to use a URL like this: /v1/magazines?page=1&per-page=50
to generate JSON results like this:
{
"results": [
{
"Programming Today",
"publisher": "Harper Collins"
},
{
"title": "Mountain Biking",
"publisher": "Outdoors Limited"
}
],
"pagination": {
"count": 2,
"page": 1,
"per_page": 50,
"pages": 1
}
}
Let’s get straight to the code and write a basic example.
@app.route("/magazines")
def magazines():
# process query parameters
page = request.args.get("page", 1, type=int)
per_page = request.args.get("per-page", 100, type=int)
# query
magazines = Magazine.query.paginate(page, per_page)
# combine results with pagination
results = {
"results": [{"title": m.title, "publisher": m.publisher} for m in magazines.items],
"pagination": {
"count": magazines.total,
"page": page,
"per_page": per_page,
"pages": magazines.pages,
},
}
return jsonify(results)
That’s it! The paginate method on our sqlalchemy query is giving us a lot of flexibility with very little code, returning a limited result set, displaying total records available, while calculating remaining pages based on our per_page setting.
Structuring our API this way allows us to maintain performance by returning small chunks of data at a time, while allowing systems to page through all of the records if needed.
The pagination format in JSON is a simplified version of the White House API standards. While the White House prefers offset and limit for pagination, I think page and per_page is more intuitive.
Here are some ways we can improve this example:
- Validate per_page parameter and set limits so someone cannot enter a high number (for example, above 100)
- Move our Magazine object into its own schema using marshmallow
- Add link headers which will allow some automated tools to page through results using headers
I’ve gone ahead and made these improvements. You can see a full working GitHub gist here.