Offset vs Cursor Pagination in GraphQL: Key Differences and Usage
offset pagination uses page numbers or offsets to fetch data slices, while cursor pagination uses unique cursors to navigate through data. Cursor pagination is more reliable for dynamic data and large datasets, avoiding duplicates or missing items that offset pagination can cause.Quick Comparison
Here is a quick side-by-side comparison of offset and cursor pagination in GraphQL.
| Factor | Offset Pagination | Cursor Pagination |
|---|---|---|
| Method | Uses numeric offset and limit | Uses unique cursor tokens |
| Performance | Can be slower on large datasets | Efficient for large or changing data |
| Reliability | May skip or duplicate items if data changes | Consistent results despite data changes |
| Complexity | Simple to implement | More complex to implement |
| Use Case | Small static datasets or simple apps | Large, dynamic datasets or real-time apps |
Key Differences
Offset pagination works by specifying a starting point (offset) and how many items to fetch (limit). It is easy to understand and implement but can cause problems when data changes between requests, leading to skipped or repeated items.
Cursor pagination uses a unique identifier (cursor) from the last fetched item to get the next set. This method handles data changes gracefully and is more efficient for large datasets because it avoids counting or skipping rows.
While offset pagination is straightforward, cursor pagination requires managing cursors and encoding/decoding them, which adds complexity but improves reliability and performance in many real-world GraphQL APIs.
Code Comparison
Example of offset pagination in a GraphQL resolver fetching posts.
const resolvers = { Query: { posts: async (_, { offset = 0, limit = 10 }, { db }) => { return await db.post.findMany({ skip: offset, take: limit, orderBy: { createdAt: 'desc' } }); } } };
Cursor Pagination Equivalent
Example of cursor pagination in a GraphQL resolver fetching posts.
const resolvers = { Query: { posts: async (_, { after, limit = 10 }, { db }) => { const cursorOptions = after ? { cursor: { id: after }, skip: 1 } : {}; return await db.post.findMany({ ...cursorOptions, take: limit, orderBy: { createdAt: 'desc' } }); } } };
When to Use Which
Choose offset pagination when your dataset is small, static, or when you need a simple implementation without complex cursor management. It works well for basic apps or admin panels.
Choose cursor pagination for large, frequently changing datasets or real-time applications where data consistency and performance matter. It prevents skipping or duplicating items and scales better with data growth.