How to Implement Pagination in GraphQL
Pagination is a common requirement in APIs to manage large datasets efficiently. In GraphQL, pagination can be implemented in several ways, with the most popular methods being offset-based pagination and cursor-based pagination. This guide will explain both methods and provide sample code for each.
1. Offset-Based Pagination
Offset-based pagination involves specifying a starting point (offset) and a limit on the number of items to return. This method is straightforward but can become inefficient with large datasets, as it requires counting all previous items.
Schema Definition
type User {
id: ID!
name: String!
email: String!
}
type Query {
users(offset: Int, limit: Int): [User ]
}
Sample Resolver
Below is an example of how to implement the users
query with offset-based pagination:
const users = [
{ id: '1', name: 'Alice', email: 'alice@example.com' },
{ id: '2', name: 'Bob', email: 'bob@example.com' },
{ id: '3', name: 'Charlie', email: 'charlie@example.com' },
// ... more users
];
const resolvers = {
Query: {
users: (parent, { offset = 0, limit = 10 }) => {
return users.slice(offset, offset + limit);
},
},
};
Sample Query
Here’s how you might query for users with pagination:
{
users(offset: 0, limit: 2) {
id
name
email
}
}
Sample Response
The server's response to the above query might look like this:
{
"data": {
"users": [
{
"id": "1",
"name": "Alice",
"email": "alice@example.com"
},
{
"id": "2",
"name": "Bob",
"email": "bob@example.com"
}
]
}
}
2. Cursor-Based Pagination
Cursor-based pagination uses a unique identifier (cursor) to mark the position of the last item returned. This method is more efficient for large datasets, as it avoids counting items and allows for more stable pagination when items are added or removed.
Schema Definition
type User {
id: ID!
name: String!
email: String!
}
type UserConnection {
users: [User ]
hasNextPage: Boolean!
endCursor: String
}
type Query {
users(first: Int, after: String): UserConnection
}
Sample Resolver
Below is an example of how to implement the users
query with cursor-based pagination:
const resolvers = {
Query: {
users: (parent, { first = 10, after }) => {
const startIndex = after ? users.findIndex(user => user.id === after) + 1 : 0;
const paginatedUsers = users.slice(startIndex, startIndex + first);
const hasNextPage = startIndex + first < users.length;
const endCursor = paginatedUsers.length > 0 ? paginatedUsers[paginatedUsers.length - 1].id : null;
return {
users: paginatedUsers,
hasNextPage,
endCursor,
};
},
},
};
Sample Query
Here’s how you might query for users with cursor-based pagination:
{
users(first: 2, after: "1") {
users {
id
name
email
}
hasNextPage
endCursor
}
}
Sample Response
The server's response to the above query might look like this:
{
"data": {
"users": {
"users": [
{
" id": "2",
"name": "Bob",
"email": "bob@example.com"
},
{
"id": "3",
"name": "Charlie",
"email": "charlie@example.com"
}
],
"hasNextPage": false,
"endCursor": "3"
}
}
}
Conclusion
Implementing pagination in GraphQL is essential for efficiently managing large datasets. Both offset-based and cursor-based pagination methods have their use cases, with offset-based being simpler and cursor-based offering better performance and stability. By understanding these methods, you can enhance the user experience of your GraphQL APIs and ensure efficient data retrieval.