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.