# Pagination in GraphQL A GraphQL query returns multiple items as a list. To implement pagination, this list can be broken into smaller lists and sent as a result of subsequent queries by the GraphQL server. There are multiple approaches to this implementation, but among the popular ones is cursor pagination. ### Cursor Pagination In cursor pagination, a "cursor" or pointer points to an item in the dataset which serves as the starting point for reads. When a query is sent from the client, the number of items requested is specified as arguments. If you want to query for items after a particular item, then the cursor position after the last item can be used as the starting point for the next set of items. Before execution, the query will be validated against the GraphQL schema. If valid, a resolver function executes that fetches items and returns them as response to the client. ![Pagination](https://hackmd.io/_uploads/HkTqC0APh.png) The diagram above illustrates how a query can be paginated into multiple queries using cursor pagination. ### Cursor Connection pagination To better represent relationships in GraphQL, Relay, a GraphQL client framework built by the team at Meta utilizes a pagination convention known as connections. The [Connection specification](https://relay.dev/graphql/connections.htm) describes this pagination convention and illustrates it's use for other clients. The main idea behind the connection pagination is to represent lists as connections. Connections include metadata about the list - the total number of items and edges that consist of nodes and their respective cursor positions. Nodes represent the actual information for each member of the list, while edges represent the relationship with the node. To represent items as connections, you have to define them in the schema as shown below: ``` type Query { items(first: Int, after: String): ItemConnection } type ItemConnection { edges: [ItemEdge!]! pageInfo: PageInfo! totalCount: Int! } type ItemEdge { node: Item! cursor: String! } type Item { id: ID! name: String! brand: String! description: String! } ``` These types can be queried to obtain item information as follows: ``` itemConnection { itemEdges { itemNode cursor } pageInfo { startCursor hasNextPage ... } } ``` Connections are built on top of cursor pagination. It uses an opaque cursor to decouple the underlying implementation from the pagination logic. It may seem like a complex model for a straightforward use case but the node and edge objects help make a clear distinction between the item and it's relationship. For instance, the edge maybe a list of itemsSold and the node may represent a single item. Data such as the time the item was sold does not belong in an item, it belongs to the relationship. Such data can be represented as a field in the edge. **Example** The [GitHub public API](https://docs.github.com/en/graphql/overview/explorer) uses connection pagination. The following query paginates a list of watchers for a repository on GitHub. ```graphql { repository(name: "marketing", owner: "graphql") { watchers(first: 2) { totalCount edges { cursor node { bio } } pageInfo { endCursor hasNextPage } } } } ``` **Query Response** ```json { "data": { "repository": { "watchers": { "totalCount": 14, "edges": [ { "cursor": "Y3Vyc29yOjQ5MDM4", "node": { "bio": "Current making games stuff with a friend.\r\n\r\nEx-TypeScript. Contributed to: Shiki, Shiki Twoslash, Danger, CocoaPods, Jest, GraphQL, RxSwift & Svelte" } }, { "cursor": "Y3Vyc29yOjUwMTMw", "node": { "bio": "Director of @graphql Foundation. Product Eng Lead at @watershed-climate.\r\n\r\nPreviously @robinhoodmarkets, @facebook, @tc39, @nytimes" } } ], "pageInfo": { "startCursor": "Y3Vyc29yOjQ5MDM4" "endCursor": "Y3Vyc29yOjUwMlsq", "hasNextPage": true } } ``` The pageInfo metadata object contains a startCursor and an endCursor value, allowing us to traverse the list in reverse too. The GitHub API has an object of type "ReactingUserEdge" for users that reacted to comments. This edge includes the node and cursor and a “reactedAt” field that speaks more about the relationship between the reaction and the user rather than just the user. ## Summary - Pagination helps keep performance in check by breaking large lists into manageable chunks called pages. - Resolver functions can use the pagination parameters provided in the query to determine the appropriate offset or cursor to fetch the desired page of records. - Cursor connection pagination helps establish consistency across GraphQL APIs with the help of the spec. It is a flexible way to traverse mutable large lists forwards and backward.