intermediate
Pagination, filtering, and sorting
Design stable list endpoints with cursor or offset pagination, bounded filters, deterministic sorting, and clear metadata.
List endpoints need stable contracts: bounded page size, deterministic sort, explicit filters, and metadata clients can rely on for sync and UI.
**Offset pagination** — simple but drifts under concurrent writes:
GET /products?limit=20&offset=40&sort=-createdAt&category=shoes
Response metadata: `{ items, total, limit, offset }`.
**Cursor pagination** — stable under churn; encode opaque cursor (often sort key + id):
GET /products?limit=20&cursor=eyJjcmVhdGVkQXQiOi4uLn0&sort=-createdAt
Response: `{ items, nextCursor, hasMore }`. Do not expose raw internal offsets in cursors.
Filtering rules:
- Whitelist filter fields and operators (`status=eq:open`, `price=lte:100`).
- Cap `limit` (e.g. max 100).
- Document default sort when omitted.
On interviews: compare offset vs cursor for live feeds, how sort ties break (secondary key), and why unindexed filters hurt production.
Common pitfalls: unlimited page size, sorting without index support, filters that allow full table scans, and cursors that leak PII or internal ids without signing.
The trade-off is cursor complexity versus offset UX (jump to page N). Hybrid: cursor for APIs, offset only for small admin datasets.
Checklist:
- Enforce max limit server-side.
- Deterministic sort with tie-breaker.
- Whitelist filters; reject unknown params explicitly or ignore consistently.
- Return pagination metadata in a stable shape.