Pagination

Learn how to use GraphQL pagination to limit the amount of results and use cursor-based pagination

How pagination works

When working with our GraphQL API, it is important to efficiently manage the amount of data returned in each query. Pagination enables you to split large result sets into smaller, more manageable chunks, which improves performance and allows for better control of the data being processed or displayed.

In our GraphQL API, pagination follows the Relay Cursor Connections Specification, which is commonly used in GraphQL for managing large lists of data. This approach relies on cursors rather than traditional numeric offsets, making it more efficient for navigating large datasets, even when items are inserted or removed.

The key concepts involved in pagination are:

  • Edges and nodes: Each paginated connection consists of a collection of edges. Each edge contains a node, which represents an individual item in the list.

  • Cursors: Each edge has a unique cursor, which identifies the position of an item within the result set. This cursor is used to move forward or backward through the list of results.

  • Limits: You can control how many items are fetched by specifying a limit (the number of results you want in a single query).

Pagination can be handled in two primary ways: forward pagination and backward pagination.

Pagination Types

Forward Pagination

Forward pagination is used when you want to start from a particular point in a dataset and move forward to retrieve the next set of results. Parameters:

  • first: This parameter specifies the number of items to fetch after the starting point (cursor).

  • after: This parameter is used to specify the cursor after which the query should start fetching items.

Example:

query ($first: Int!, $after: String) {
  invoices(first: $first, after: $after) {
    nodes {
      id
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Variables:

{
  "first": 2,
  "after": null
}

Response:

{
  "data": {
    "invoices": {
      "nodes": [
        {
          "id": "1"
        },
        {
          "id": "2"
        }
      ],
      "pageInfo": {
        // The response shows that there is a next page and provides the cursor needed to use as the after input for retrieving the next set of nodes.
        "hasNextPage": true,
        "endCursor": "Y3Vyc29yLWZyb20tZGVmaW5pdGlvbjoy"
      }
    }
  }
}

In this example:

  • The first parameter requests the next 2 invoices.

  • The after parameter is not provided, so the query starts from the beginning of the dataset.

The response will include:

  • nodes: An array of invoices

  • pageInfo: Metadata about whether there are more results (hasNextPage) and the cursor of the last item (endCursor) to continue pagination.

You can retrieve the next page by reusing the same query with different variables:

Variables:

{
  "first": 2,
  "after": "Y3Vyc29yLWZyb20tZGVmaW5pdGlvbjoy"
}

Response:

{
  "data": {
    "invoices": {
      "nodes": [
        {
          "id": "3"
        },
        {
          "id": "4"
        }
      ],
      "pageInfo": {
        // The response shows that there are no more pages, indicating this is the final page of the connection.
        "hasNextPage": false,
        "endCursor": "Y3Vyc29yLWZyb20tZGVmaW5pdGlvbjo0"
      }
    }
  }
}

Backward Pagination

Backward pagination allows you to move backward in the dataset to retrieve results that were previously loaded or skipped.

Parameters:

  • last: This parameter specifies the number of items to fetch before the starting point (cursor).

  • before: This parameter is used to specify the cursor before which the query should start fetching items.

Example:

query ($last: Int!, $before: String) {
  invoices(last: $last, before: $before) {
    nodes {
      id
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Variables:

{
  "last": 2,
  "before": null
}

Response:

{
  "data": {
    "invoices": {
      "nodes": [
        {
          "id": "3",
        },
        {
          "id": "4",
        }
      ],
      "pageInfo": {
        "hasPreviousPage": true,
        "startCursor": "Y3Vyc29yLWZyb20tZGVmaW5pdGlvbjox"
      }
    }
  }
}

In this example:

  • The last parameter requests the previous 2 invoices.

  • The before parameter is not provided, so the query starts from the end of the dataset.

The response will include:

  • nodes: An array of invoices

  • pageInfo: Metadata about whether there are more results (hasPreviousPage) and the cursor of the first item (startCursor) to continue pagination.

You can retrieve the previous page by reusing the same query with different variables:

Variables:

{
  "last": 2,
  "before": "Y3Vyc29yLWZyb20tZGVmaW5pdGlvbjox"
}

Response:

{
  "data": {
    "invoices": {
      "nodes": [
        {
          "id": "1"
        },
        {
          "id": "2"
        }
      ],
      "pageInfo": {
        "hasPreviousPage": false,
        "startCursor": "yc29yLTM4OWZkNjcyLWE1YzEtNGJmY4a"
      }
    }
  }
}

Connection Edges

In connections, an Edge type represents the link between a node and its parent. Typically, querying nodes and pageInfo is recommended over querying edges. However, if you need metadata specific to an Edge, you can query edges instead. Each Edge includes at least the edge's cursor and the associated node.

Example:

query($first: Int!) {
  invoices(first: $first) {
    edges {
      cursor
      node {
        id
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Variables:

{
  "first": 2
}

Response:

{
  "data": {
    "invoices": {
      "edges": [
        {
          "cursor": "NjcyLWE1YzEtNGJmYy1iMmZkLTY0YzM5",
          "node": {
            "id": "1"
          }
        },
        {
          "cursor": "ZjgzLTdiNDctNGE1Yi05YTJmLTFkNzM4",
          "node": {
            "id": "2"
          }
        }
      ],
      "pageInfo": {
        "hasNextPage": true,
        "endCursor": "ZjgzLTdiNDctNGE1Yi05YTJmLTFkNzM4"
      }
    }
  }
}

In this example:

The PageInfo.endCursor matches the last edge's cursor, and the edges[].node list is equivalent to the nodes list in a forward pagination query.

Last updated