GraphQL in Production · Performance and Caching
Pagination with Relay Connections
Cursor-based pagination conventions most GraphQL clients understand.
The Relay Cursor Connections specification standardizes list pagination: fields take first/after (and last/before) and return a Connection with edges { cursor node } and pageInfo { hasNextPage endCursor }. Edges can carry relationship data (e.g., role on a membership). Cursors are opaque (encode keyset values), and every list must enforce a maximum first. Many clients (Relay, Apollo, urql) provide helpers for merging connection pages.
List patterns in GraphQL
| Criterion | Relay connections | Offset args (limit/offset) | Simple list |
|---|---|---|---|
| Stable with inserts | Yes | No | N/A |
| Client library support | Excellent | Manual | N/A |
| Edge metadata | Yes | No | No |
| Verbosity | Higher | Low | Lowest |
type OrderConnection { edges: [OrderEdge!]! pageInfo: PageInfo! totalCount: Int }
type OrderEdge { cursor: String! node: Order! }
type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String }
type Query { orders(first: Int = 20, after: String, filter: OrderFilter): OrderConnection! }
query { orders(first: 20, after: "b3JkXzAxSjla…") {
edges { cursor node { id status } }
pageInfo { hasNextPage endCursor }
} }
Common pitfalls
- Lists without
firstlimits. - Exposing raw offsets as cursors.
- Expensive
totalCountcomputed for every request.
Key points
- edges/node/cursor + pageInfo
- Opaque keyset cursors
- Cap first/last
Further reading: Relay Cursor Connections Specification
Practice questions
Why put a cursor on each edge instead of only in pageInfo?
Answer
Clients can resume from any item (e.g., after deleting items or for 'load more from here'), not just from the end of the page.
What kind of data belongs on an edge rather than the node?
Answer
Data about the relationship, such as a member's role in a team or when a user starred a repository.
Open in the app to mark lessons complete, take notes and track your progress.