Difficulty: Intermediate
What are the common pagination patterns in FastAPI and how do you implement them?
1. Offset-based pagination - Uses `skip` (offset) and `limit` query parameters. Simple but slow for large datasets (DB scans rows to skip).
2. Cursor-based pagination - Uses a pointer (cursor) to the last item. Much more efficient for large datasets because the DB can seek directly. Common in APIs with real-time data (timelines, feeds).
3. Page-number pagination - Uses `page` and `page_size` parameters. User-friendly but internally converts to offset.
Best practice: Encapsulate pagination logic in a reusable dependency so all list endpoints share the same interface. Return metadata (`total`, `page`, `has_next`) alongside the data.
from fastapi import FastAPI, Depends, Query
from pydantic import BaseModel
from typing import Annotated, Generic, TypeVar, List
app = FastAPI()
T = TypeVar("T")
class PaginationParams:
def __init__(
self,
page: Annotated[int, Query(ge=1)] = 1,
size: Annotated[int, Query(ge=1, le=100)] = 20,
):
self.page = page
self.size = size
self.offset = (page - 1) * size
class PaginatedResponse(BaseModel, Generic[T]):
items: List[T]
total: int
page: int
size: int
pages: int
@app.get("/users")
def list_users(pagination: PaginationParams = Depends()):
total = db.query(User).count()
users = db.query(User).offset(pagination.offset).limit(pagination.size).all()
return PaginatedResponse(
items=users,
total=total,
page=pagination.page,
size=pagination.size,
pages=(total + pagination.size - 1) // pagination.size,
)
pagination, offset, cursor, dependency, reusable