Skip to content

.tip

· one of the small things

Cursor pagination, not OFFSET, past the first few pages

OFFSET 100000 reads and throws away a hundred thousand rows to return ten. And a row inserted between two page loads shifts everything, so the client sees a duplicate or a gap.

SELECT * FROM events WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC LIMIT 50;

The cursor is the last row's (created_at, id), encoded. Every page costs the same, and the list holds still when new rows arrive.

httpapi-designpostgresql