Pagination in GraphQL
Pagination is a technique for dividing large sets of data into smaller chunks (pages), enabling efficient data fetching and navigation. In GraphQL, there are two common approaches to pagination:
- Offset-based pagination
- Cursor-based pagination
1. Offset-based Pagination
This method uses limit
and offset
arguments to paginate results.
Example Query
query GetUsers($limit: Int!, $offset: Int!) {
users(limit: $limit, offset: $offset) {
id
name
email
}
}
Variables
Pros:
- Simple to implement.
- Familiar for users coming from REST APIs or SQL.
Cons:
- Poor performance on large datasets.
- Doesn’t handle inserted/removed records gracefully.
2. Cursor-based Pagination
Cursor-based pagination uses cursors (usually unique IDs or encoded values) to fetch pages.
Relay-style Pagination Structure
type Query {
users(first: Int, after: String): UserConnection
}
type UserConnection {
edges: [UserEdge]
pageInfo: PageInfo
}
type UserEdge {
node: User
cursor: String
}
type PageInfo {
hasNextPage: Boolean
hasPreviousPage: Boolean
startCursor: String
endCursor: String
}
Example Query
query GetUsers($first: Int, $after: String) {
users(first: $first, after: $after) {
edges {
node {
id
name
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
}
}
Variables
Pros:
- More efficient for large or real-time datasets.
- Prevents issues with data shifting between pages.
Cons:
- Slightly more complex to implement and understand.
Choosing a Pagination Strategy
Feature | Offset-based | Cursor-based |
---|---|---|
Simple use case | ✅ | ❌ |
Large datasets | ❌ | ✅ |
Real-time data | ❌ | ✅ |
Backward pagination | ❌ | ✅ |
REST-like behavior | ✅ | ❌ |
Best Practices
- Prefer cursor-based pagination for APIs where performance and consistency are important.
- Encode cursors to avoid exposing database internals.
- Always return
pageInfo
metadata to help clients understand navigation state. - Document pagination behavior clearly for frontend teams.