# Cursor-Based Pagination — GraphQL

Source: https://www.skillbyai.com/en/graphql/p-pagination

> The connection pattern.

## Edges, nodes and pageInfo

Never return unbounded lists. The **connection** pattern (from the Relay specification, widely used beyond Relay) takes `first` and `after` (or `last` and `before`) arguments and returns `edges` (each with a `node` and an opaque `cursor`) plus `pageInfo { hasNextPage endCursor }`. Cursors encode a position, such as the last seen sort key, so pages stay stable when new items are inserted, unlike offset pagination. Enforce a maximum page size on the server.

## Keep it fast and predictable

Pagination, query cost limits and caching strategies protect performance as usage grows.

![Three ideas: cursor pagination, query cost and persisted queries, caching.](assets/figures/graphql/section-7-map.svg) — Figure 7.1 — Pagination, cost limits and caching.

## A connection type

GraphQL SDL.

```graphql
type OrderConnection {
  edges: [OrderEdge!]!
  pageInfo: PageInfo!
  totalCount: Int
}

type OrderEdge {
  cursor: String!
  node: Order!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}
```

## Make totalCount optional

Counting millions of rows is expensive; compute it only when the client asks for it.

**Quiz:** What does a client pass to get the next page in cursor pagination?

- [ ] Nothing; pages are automatic
- [ ] page: 2
- [ ] The full previous result
- [x] after: the endCursor from the previous page

*Answer:* after: the endCursor from the previous page. Opaque cursors mark positions.
