Pagination¶
Prerequisites¶
Before reading this chapter, you should be familiar with:
- REST APIs
- HTTP Basics
- SQL (
SELECT,WHERE,ORDER BY,LIMIT) - Database Indexes (recommended)
Why Do We Need Pagination?¶
Let's begin with a simple query.
Suppose this customer has placed only 5 orders.
The query returns 5 rows and everything works perfectly.
Now imagine the same customer has been using your application for several years and has accumulated 15,000 orders.
The query hasn't changed.
The amount of data has.
Now think from the user's perspective.
When the customer opens the My Orders page, do they actually see all 15,000 orders?
Of course not.
Almost every application displays only a small portion of the data at a time.
┌──────────────────────────────┐
│ My Orders │
├──────────────────────────────┤
│ Order #15421 │
│ Order #15420 │
│ Order #15419 │
│ ... │
│ Order #15402 │
├──────────────────────────────┤
│ Previous Next │
└──────────────────────────────┘
Typically, the UI displays somewhere between 10 and 50 records on a single screen.
Fetching all 15,000 rows just to display the first 20 is wasteful.
Every layer of the system performs unnecessary work.
- The database spends time producing thousands of rows.
- The application allocates memory to hold those rows.
- The response must be serialized into JSON (or another response format).
- Thousands of records are transferred over the network.
- The client receives data it never renders.
Most of this work provides no value because the user only looks at a tiny fraction of the dataset.
Instead, we retrieve only the rows required to render the current screen.
The server returns only the first 20 records.
When the user clicks Next, the application requests the next 20.
This technique of retrieving data in small chunks instead of loading the entire dataset is called pagination.
Benefits of Pagination¶
Pagination provides several advantages:
- Reduces database work
- Reduces network bandwidth
- Keeps API response times predictable
- Reduces memory usage on both the server and client
- Improves user experience
- Makes APIs scalable as datasets continue to grow
Types of Pagination¶
Most backend systems use one of the following pagination strategies:
- Offset Pagination
- Cursor (Keyset) Pagination
- Token Pagination
We'll discuss each of them individually.
Offset Pagination¶
Offset pagination is the simplest and most commonly used pagination strategy.
The client specifies two values:
- limit – maximum number of rows to return
- offset – number of rows to skip before returning results
Example request:
The backend executes:
SELECT
order_id,
created_at,
updated_at,
order_info
FROM orders
WHERE user_id = 'abc123'
ORDER BY created_at DESC
LIMIT 20 OFFSET 40;
Here,
OFFSET 40tells the database to skip the first 40 rows in the ordered result set.LIMIT 20tells it to return only the next 20 rows.
Conceptually,
This makes implementing page-based navigation straightforward.
For example,
The offset can easily be calculated as:
Why Does OFFSET Become Slow?¶
One common misconception is that the database can directly jump to the requested offset.
For example,
does not mean
Jump directly to row 1,000,001.
Instead, the database must advance through the first one million rows of the ordered result before it can begin returning results.
Conceptually,
If an index exists, the database usually traverses the index rather than scanning the entire table, but it still needs to walk through the skipped entries.
As the offset grows, the amount of work also grows.
For small offsets, this overhead is negligible.
For very large offsets, it becomes increasingly expensive.
Problems with Offset Pagination¶
1. Performance Degrades on Deep Pages¶
Suppose your table contains 50 million rows.
Requesting
requires traversing approximately forty million ordered entries before returning just twenty rows.
The deeper the page, the slower the query becomes.
2. Unstable Under Concurrent Writes¶
Offset pagination assumes that the dataset remains unchanged between requests.
In production systems, this assumption rarely holds.
Imagine Page 1 returns
Before the user requests Page 2, another order is inserted at the beginning.
The dataset now becomes
The second request still asks for
The results now become
Notice that 5 appears twice.
Similarly, if rows are deleted, some records may never appear in the paginated results.
This is one of the major limitations of offset pagination.
Cursor (Keyset) Pagination¶
Cursor pagination approaches the problem differently.
Instead of asking
Skip the first N rows
it asks
Continue after this specific row.
Suppose the last row returned has
The next request becomes
The backend executes
SELECT
order_id,
created_at,
updated_at,
order_info
FROM orders
WHERE id > 100
ORDER BY id
LIMIT 20;
The response contains
The client stores this cursor and sends it back when requesting the next page.
Why Cursor Pagination Scales Better¶
Unlike offset pagination,
allows the database to use the index to seek directly to the first matching row.
Instead of traversing one million previous rows,
the database jumps directly to
and begins reading from there.
The amount of work remains roughly constant regardless of how deep into the dataset the user navigates.
Why Cursor Pagination Is Stable¶
Cursor pagination is anchored to an actual row rather than a row position.
Suppose the first page ends at
Now another row is inserted with
The next query still executes
The newly inserted row has no impact on the next page because the cursor always represents a fixed point in the ordered dataset.
This avoids duplicate and missing rows caused by concurrent inserts or deletes.
Composite Cursor¶
Sometimes the sorting column is not unique.
For example,
Multiple rows may share the same timestamp.
Using only
can produce duplicate or skipped records.
Instead, use a unique tiebreaker.
This guarantees a deterministic ordering across pages.
Token Pagination¶
Token pagination is built on top of cursor pagination.
Instead of exposing the cursor directly,
the server encodes the cursor into an opaque token.
The client never interprets or modifies this value.
It simply sends it back.
The server decodes the token and continues fetching the next page.
This approach allows the server to change the cursor format without affecting clients.
Offset vs Cursor¶
| Feature | Offset | Cursor |
|---|---|---|
| Implementation | Simple | Moderate |
| Performance on deep pages | Degrades with offset | Consistent |
| Stable under inserts/deletes | No | Yes |
| Supports arbitrary page numbers | Yes | No |
| Suitable for infinite scrolling | No | Yes |
| Typical use case | Admin dashboards | Public APIs |
Which Pagination Strategy Should You Choose?¶
Offset Pagination¶
Use when:
- The dataset is relatively small.
- Users need page numbers.
- Jumping to arbitrary pages is required.
- Data changes infrequently.
- Internal dashboards and reporting tools.
Cursor Pagination¶
Use when:
- Tables contain millions of rows.
- APIs receive high traffic.
- Data changes frequently.
- Infinite scrolling is used.
- Consistent performance is required.
Token Pagination¶
Use when:
- Building public APIs.
- Cursor implementation details should remain hidden.
- The server needs flexibility to evolve the cursor format.
Common Mistakes¶
Returning Unlimited Data¶
Never allow
Always enforce a maximum page size on the server.
Sorting by Non-Unique Columns¶
Always include a unique tiebreaker such as the primary key.
Exposing Cursor Internals¶
Treat cursors as opaque values.
Clients should never construct or modify them.
Using Offset Pagination for Large Public APIs¶
Offset pagination is easy to implement but does not scale well for deep pages or frequently changing datasets.
Key Takeaways¶
- Pagination limits the amount of data returned by an API regardless of dataset size.
- Offset pagination is simple but becomes slower as the offset increases.
- Offset pagination may produce duplicate or missing rows when data changes between requests.
- Cursor pagination anchors results to the last row returned, providing stable and scalable pagination.
- Token pagination is a cursor-based approach that hides pagination details from clients.
- Choose the pagination strategy based on the size of the dataset, traffic patterns, and user experience requirements rather than using a single approach everywhere.