Card 03/ 08

ExampleDifficulty: Intermediate1 min

Cursor Pagination Example: Every Order After id 4180

The last row you saw becomes the place the next page starts.

Cursor pagination asks for a page by pointing at the last row the client saw, and returns the rows after it. This example fetches the page of orders that follows order 4180, shows the request, the SQL, and what the cursor holds, and then the same idea in Stripe's API.

The request. The previous page ended on order 4180. Its response included a cursor for that row, and the client sends it back.

text
GET /orders?limit=25&cursor=eyJpZCI6NDE4MH0=
The request for the next page

What the cursor holds. The cursor is the last row's sort key, encoded so the client treats it as an opaque token rather than something to edit. Decoded from Base64, this one is {"id":4180}. Encoding it lets the server change what goes inside later, a date as well as the id, without changing the API.

sql
SELECT id, customer_id, total
FROM orders
WHERE id > 4180
ORDER BY id
LIMIT 25;
The query the server runs for it

Why it stays fast. With an index on id, the database goes straight to 4180 in the index and reads the next 25 entries. It never touches the rows before it, so the thousandth page costs what the first does. Databases call this a keyset or seek query.

A real API. Stripe's list endpoints work this way: GET /v1/customers?limit=3&starting_after=cus_123 returns the customers after that one, and the response's has_more field says whether another page exists.

What it gives up. There is no page 12: to reach it, the client must walk pages 1 to 11, because each cursor comes from the page before.

So with cursor pagination, each page hands the client a pointer to its last row, and the next query seeks past it through an index, at the same cost on every page.