Cursor pagination (recommended)
- Stable under inserts/deletes
- Uses an opaque cursor (e.g., last seen id + sort key)
- Easy to cache and resume
SELECT *
FROM items
WHERE (created_at, id) < (:created_at, :id)
ORDER BY created_at DESC, id DESC
LIMIT 50;Offset pagination (avoid at scale)
- Can skip/duplicate rows when data changes
- Gets slower as offset grows
If you need “page numbers”, store cursors per page server-side.