Skip to main content

Task 07 — Add Pagination to an Existing API

Difficulty: Intermediate
Goal: Extend an existing list endpoint (e.g. the users list) to support cursor-based or offset-based pagination.


What you'll learn
  • How to handle page / limit query parameters in a Flask-RESTX route
  • How to write a Cosmos DB query with OFFSET … LIMIT or a continuation token
  • How to return pagination metadata alongside data

Endpoint spec

Extend your task-03 endpoint (or build a new one) to accept:

ParamTypeDefaultDescription
pageint1Page number (1-indexed)
limitint20Items per page (max 100)

Response shape:

{
"status": "success",
"status_code": 200,
"data": {
"items": [ ... ],
"page": 1,
"limit": 20,
"total": 142,
"totalPages": 8
}
}

Implementation notes

Query with OFFSET / LIMIT in Cosmos DB

Cosmos DB SQL supports OFFSET … LIMIT natively. ORDER BY must always be included — without it the page order is non-deterministic.

The existing get_all_users_of_industry(industry_id) returns id, username, type, active. Add a paginated variant alongside it:

@staticmethod
@validate_values()
def get_users_paginated(industry_id, offset, limit):
return (
f'SELECT c.id, c.username, c.type, c.active FROM c '
f'WHERE c.industryId = "{industry_id}" '
f'ORDER BY c._ts DESC OFFSET {offset} LIMIT {limit}'
)

Count query

@staticmethod
@validate_values()
def count_users_by_industry(industry_id):
return f'SELECT VALUE COUNT(1) FROM c WHERE c.industryId = "{industry_id}"'
User types

Users have a type field — DEFAULT, OTP, SSO, or microsoft. The existing service counts users by summing OTP + SSO counts via separate queries. For pagination you can query all types together with a single industryId filter.

Route changes

page  = int(request.args.get('page', 1))
limit = min(int(request.args.get('limit', 20)), 100) # cap at 100
offset = (page - 1) * limit

Edge cases to handle
  • page=0 or negative → return 400 with a clear message
  • limit > 100 → clamp to 100 (don't error, just cap)
  • Page beyond total → return empty items list, not an error

Done when
  • ?page=1&limit=5 returns the first 5 items
  • ?page=2&limit=5 returns items 6–10
  • totalPages is calculated correctly
  • Invalid page=0 returns a 400 response