Skip to content

Commit 1839934

Browse files
committed
Editorial to #4391
1 parent 02179ff commit 1839934

File tree

1 file changed

+28
-7
lines changed

1 file changed

+28
-7
lines changed

website/pages/docs/cursor-based-pagination.mdx

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
title: Implementing Cursor-based Pagination
33
---
44

5+
import { Callout } from "nextra/components";
6+
57
When a GraphQL API returns a list of data, pagination helps avoid
68
fetching too much data at once. Cursor-based pagination fetches items
79
relative to a specific point in the list, rather than using numeric offsets.
@@ -18,15 +20,15 @@ that works well with clients.
1820

1921
Cursor-based pagination typically uses a structured format that separates
2022
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
2224
this format originated in Relay, many GraphQL APIs use it independently because of its
2325
clarity and flexibility.
2426

2527
This pattern wraps your list of items in a connection type, which includes the following fields:
2628

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.
3032
- `pageInfo`: Metadata about the list, such as whether more items are available.
3133

3234
The following query and response show how this structure works:
@@ -192,7 +194,7 @@ const usersField = {
192194
let start = 0;
193195
if (args.after) {
194196
const index = decodeCursor(args.after);
195-
if (index != null) {
197+
if (Number.isFinite(index)) {
196198
start = index + 1;
197199
}
198200
}
@@ -243,7 +245,7 @@ async function resolveUsers(_, args) {
243245

244246
if (args.after) {
245247
const index = decodeCursor(args.after);
246-
if (index != null) {
248+
if (Number.isFinite(index)) {
247249
offset = index + 1;
248250
}
249251
}
@@ -279,6 +281,25 @@ an `OFFSET`. To paginate backward, you can reverse the sort order and slice the
279281
results accordingly, or use keyset pagination for improved performance on large
280282
datasets.
281283
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 indicies
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+
282303
## Handling edge cases
283304
284305
When implementing pagination, consider how your resolver should handle the following scenarios:
@@ -297,7 +318,7 @@ errors.
297318
298319
To learn more about cursor-based pagination patterns and best practices, see:
299320
300-
- [Relay Cursor Connections Specification](https://relay.dev/graphql/connections.htm)
321+
- [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm)
301322
- [Pagination](https://graphql.org/learn/pagination/) guide on graphql.org
302323
- [`graphql-relay-js`](https://github.com/graphql/graphql-relay-js): Utility library for
303324
building Relay-compatible GraphQL servers using GraphQL.js

0 commit comments

Comments
 (0)