core practice · coding problem

Cursor pagination that survives inserts

Implement page(rows, cursor, limit) for keyset pagination. Rows are {id, createdAt} sorted by (createdAt, id) ascending. cursor is null for the first page or {createdAt, id} for 'resume strictly after this'. Return { items, nextCursor } where nextCursor is the last item's key, or null when the page is not full.

The mechanism

OFFSET makes the database produce and discard every skipped row, so page 10,000 costs proportionally more than page 1 — and because the underlying rows shift between requests, an insert can make a client see the same item twice or miss one entirely. Cursor (keyset) pagination replaces 'skip N' with 'resume after this key', turning every page into an index seek at constant cost and making the sequence stable under concurrent writes. The price is that you can no longer jump to an arbitrary page number.

Study Pagination

Expected evidence

Two sequential pages return disjoint items even when a row is inserted between the calls.

Try this drill in the practice workspace →

Hints when you need them
  • 'Strictly after' on a composite key: createdAt greater, OR createdAt equal and id greater. The tiebreak is what makes it total.
  • Comparing on createdAt alone breaks as soon as two rows share a timestamp.

Watch for these mistakes

  • OFFSET on a large table, where deep pages degrade linearly
  • Paginating without a total ordering — ties on the sort key make results non-deterministic across pages
  • Exposing a raw primary key as the cursor, leaking row counts and creation order
  • Returning a total count on every page, which often costs more than the page itself

Browse all practice briefs