2
2
title : Implementing Cursor-based Pagination
3
3
---
4
4
5
+ import { Callout } from " nextra/components" ;
6
+
5
7
When a GraphQL API returns a list of data, pagination helps avoid
6
8
fetching too much data at once. Cursor-based pagination fetches items
7
9
relative to a specific point in the list, rather than using numeric offsets.
@@ -18,15 +20,15 @@ that works well with clients.
18
20
19
21
Cursor-based pagination typically uses a structured format that separates
20
22
pagination metadata from the actual data. The most widely adopted pattern follows the
21
- [ Relay Cursor Connections Specification] ( https://relay.dev/graphql/connections.htm ) . While
23
+ [ GraphQL Cursor Connections Specification] ( https://relay.dev/graphql/connections.htm ) . While
22
24
this format originated in Relay, many GraphQL APIs use it independently because of its
23
25
clarity and flexibility.
24
26
25
27
This pattern wraps your list of items in a connection type, which includes the following fields:
26
28
27
- - ` edges ` : A list of edge objects, each representing an item in the list.
28
- - ` node ` : The actual object you want to retrieve, such as user, post, or comment.
29
- - ` cursor ` : An opaque string that identifies the position of the item in the list.
29
+ - ` edges ` : A list of edge objects, representing for each item in the list:
30
+ - ` node ` : The actual object you want to retrieve, such as user, post, or comment.
31
+ - ` cursor ` : An opaque string that identifies the position of the item in the list.
30
32
- ` pageInfo ` : Metadata about the list, such as whether more items are available.
31
33
32
34
The following query and response show how this structure works:
@@ -192,7 +194,7 @@ const usersField = {
192
194
let start = 0 ;
193
195
if (args .after ) {
194
196
const index = decodeCursor (args .after );
195
- if (index != null ) {
197
+ if (Number . isFinite ( index) ) {
196
198
start = index + 1 ;
197
199
}
198
200
}
@@ -243,7 +245,7 @@ async function resolveUsers(_, args) {
243
245
244
246
if (args .after ) {
245
247
const index = decodeCursor (args .after );
246
- if (index != null ) {
248
+ if (Number . isFinite ( index) ) {
247
249
offset = index + 1 ;
248
250
}
249
251
}
@@ -279,6 +281,25 @@ an `OFFSET`. To paginate backward, you can reverse the sort order and slice the
279
281
results accordingly, or use keyset pagination for improved performance on large
280
282
datasets.
281
283
284
+ <Callout type='info'>
285
+
286
+ The above is just an example to aid understanding; in a production application,
287
+ for most databases it is better to use ` WHERE ` clauses to implement cursor
288
+ pagination rather than using ` OFFSET ` . Using ` WHERE ` can leverage indices
289
+ (indexes) to jump directly to the relevant records, whereas ` OFFSET ` typically
290
+ must scan over and discard that number of records. When paginating very large
291
+ datasets, ` OFFSET ` can become more expensive as the value grows, whereas using
292
+ ` WHERE ` tends to have fixed cost. Using ` WHERE ` can also typically handle the
293
+ addition or removal of data more gracefully.
294
+
295
+ For example, if you were ordering a collection of users by username, you could
296
+ use the username itself as the ` cursor` , thus GraphQL's ` allUsers (first: 10 ,
297
+ after: $cursor)` could become SQL's ` WHERE username > $1 LIMIT 10 ` . Even if
298
+ that user was deleted, you could still continue to paginate from that position
299
+ onwards.
300
+
301
+ </Callout>
302
+
282
303
## Handling edge cases
283
304
284
305
When implementing pagination, consider how your resolver should handle the following scenarios:
@@ -297,7 +318,7 @@ errors.
297
318
298
319
To learn more about cursor-based pagination patterns and best practices, see:
299
320
300
- - [Relay Cursor Connections Specification](https://relay.dev/graphql/connections.htm)
321
+ - [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm)
301
322
- [Pagination](https://graphql.org/learn/pagination/) guide on graphql.org
302
323
- [` graphql- relay- js` ](https://github.com/graphql/graphql-relay-js): Utility library for
303
324
building Relay-compatible GraphQL servers using GraphQL.js
0 commit comments