API Style & Pattern Comparisons
Side-by-side trade-offs. Neither column wins — the consumer environment, the data shape and the operational budget decide.
Offset pagination vs Cursor pagination
page/limit arithmetic that any developer understands in seconds vs an opaque continuation token that stays correct and fast while the data moves underneath it.
Small, slow-changing datasets where users genuinely need page numbers and jump-to-page — admin tables, reports.
The collection takes concurrent writes — rows shift between requests, so pages duplicate and skip items.
Trivial to implement, stateless, maps directly to numbered-page UI, random access to any page.
OFFSET 100000 makes the database scan and discard 100k rows; deep pages get slower page by page.
Deep-offset queries degrade the database for everyone; drift complaints surface as support tickets.
Large or live collections: feeds, logs, event lists — anything scrolled while rows are inserted and deleted.
The UI is built on numbered pages and "jump to page 47" — a cursor can't take you there.
Stable under inserts and deletes, O(1)-ish continuation via the index at any depth, hides internals behind an opaque token.
The sort key isn't unique or stable — the cursor lands ambiguously and rows repeat or vanish.
Cursor encoding/versioning, a documented "cursor expired" error path, and a tiebreaker column in every sort.
| Dimension | Offset pagination | Cursor pagination |
|---|---|---|
| Random access | Any page, directly | Next (and maybe previous) only |
| Cost at depth | Grows linearly — scan and discard | Flat — index seek from the cursor key |
| Under concurrent writes | Duplicates and missing rows between pages | Stable continuation from a fixed position |
| Client contract | Transparent page/limit numbers clients compute | Opaque token clients must echo back unchanged |
| Retrofit cost | Easy to ship first, painful to outgrow | More upfront design, no forced migration later |