Implementing Cursor-based Pagination For Every GraphQL API
Published: 2022-10-17 12:02:26

Implementing Cursor-based Pagination For Every GraphQL API
==========================================================

Backends often return a massive amount of data, and intaking all data simultaneously causes more overhead and increases the response time. Pagination preserves the application's performance by receiving small pieces of data in subsequent requests until the entire dataset is received.

When Facebook publicly released GraphQL as a client-driven API-based query language, it quickly gained hype because it allowed front-end developers to modify their backends easily. Applications using GraphQL are more efficient and work promptly on slow networks, and therefore, it is quickly replacing the traditional API architectures like REST. Pagination is an essential concept in GraphQL, but there aren't many resources on pagination in GraphQL.

In this post, we'll compare the different ways to handle pagination in GraphQL and learn how to configure a REST directive to perform cursor-based pagination for every REST API using StepZen.

> **TL;DR**: You can find the complete documentation on paginating GraphQL using StepZen [here](https://stepzen.com/docs/connecting-backends/rest-services-graphql-pagination).

Comparing Different Methods of Pagination in GraphQL
----------------------------------------------------

Pagination in GraphQL is not different from pagination in REST APIs, although some types of pagination better fit GraphQL. Before discussing the preferred pagination method in GraphQL, let's look at different pagination types. Typically, APIs offer three methods for pagination. These are:

*   [Offset Pagination](https://stepzen.com/blog#1-offset-pagination)
*   [Page Number Pagination](https://stepzen.com/blog#2-page-number-pagination)
*   [Cursor Pagination](https://stepzen.com/blog#3-cursor-pagination)

### 1\. Offset Pagination

Offset pagination consists of two primary parameters: limit and offset. The limit indicates the maximum number of results to show. And the offset denotes the position in the list from where the pagination starts.

Suppose you have a list of 100 students. If you set the limit parameter to 10 and offset to 20, the database engine will count from the 20th student and display the following ten students in each iteration. For instance, the first iteration will show the students from 20 to 30, then from 30 to 40, etc.

Although offset pagination is the most straightforward, it has a significant drawback. When some items are added or deleted, offset pagination results in repeated or missing data values.

**Pros:**

*   Most common way to do pagination in general.
*   Relatively simple implementation.
*   Most SQL-based databases support the limit and the offset variables, so it's easier to map values.

**Cons:**

*   Prone to data inconsistencies (repeated or missing data).
*   It doesn't provide information about more pages, total pages, or retrieval of previous pages.

### 2\. Page Number Pagination

As the name specifies, page number pagination returns a single page per request. You all have seen a "next page" option when working with tables; each sheet shows the same number of results or entries. Similarly, page number pagination returns the same number of results per request.

Let's take an example to understand how it works. Page number pagination uses the after parameter to indicate the starting point for pagination. For instance, if you have a list of 30 students and set the after and the limit parameters to 23 and 5, respectively, then the output list will show a list of 5 students from the 23rd to 28th entry.

Page number pagination is more reliable as the upcoming results start from the last fetched values.

**Pros:**

*   Easier to implement.
*   It doesn't require complex logical analysis.

**Cons:**

*   Data inconsistencies.

### 3\. Cursor Pagination

The third kind of pagination is cursor-based pagination. However, it is most complicated but is adaptable to dynamic data. Therefore, it is the preferred way to do pagination in GraphQL.

This method includes a specific parameter for the cursor. A cursor is nothing but a reference point that shows the position of an item in the database. The data are represented as nodes and a cursor as an edge in the graphical representation.

A cursor is a base64 encoded number. The query written for cursor-based pagination returns an object representation instead of a list.

**Pros:**

*   The preferred way to do pagination in GraphQL.
*   Provides valuable data for UX uses.
*   Allows reverse pagination.
*   No issues in the case of dynamic data as pagination are done to a specific row.

**Cons:**

*   It doesn't allow random access.
*   Needs complicated queries.

How to Implement Pagination in GraphQL using StepZen
----------------------------------------------------

Now you know about all the possible pagination methods and the preferred way of doing pagination in GraphQL, which is cursor-based pagination. If your REST API supports offset or page number pagination, you can easily implement cursor-based pagination in GraphQL using StepZen.

The two essential parameters you need to specify for every type of pagination are `type` and `setters`. The `type` parameters define the pagination style you want to implement. It has three possible values: `PAGE_NUMBER`, `OFFSET`, and `NEXT_CURSOR`. Next comes the `setters` parameters, which need to be pointed towards a field that indicates how many results or pages the response will output. You can find the list of all parameters with different pagination styles in the [documentation](https://stepzen.com/docs/connecting-backends/rest-services-graphql-pagination/).

Let's look at some examples:

### 1\. Implementing Cursor Pagination for Offset

The below snippet illustrates how to implement GraphQL cursor-based pagination using StepZen for REST APIs supporting offset pagination. Note that the parameter `first` is set to the number of required results. The second parameter, `after`, is set to the starting point of the pagination.

```graphql
customers(
  first: Int! = 20 
  after: String! = ""
): CustomerConnection
  @rest(
    endpoint:"https://api.example.com/customers?limit=$first&offset=$after"
    pagination: {
        type: OFFSET
        setters: [{field:"total" path: "meta.total_count"}]
      }
    )
```

Since it's the first request, the `after` parameter is equal to an empty string. The `first` parameter is set to `20`, which means that the first 20 results will be returned. On the second request, you can change the value for `after` to be equal to the value of `first` from the previous request. Every new request will be a multiple of the `first` parameter.

### 2\. Implementing Page Number Pagination

Take a look at the following code to get a better understanding of implementing cursor-based pagination in GraphQL using StepZen, when your REST API is relying on page number pagination:

```graphql
customers(
  first: Int! = 20 
  after: String! = ""
): CustomerConnection
  @rest(
    endpoint:"https://api.example.com/customers?page=$after&per_page=$first"
    pagination: {
        type: NEXT_CURSOR
        setters: [{field:"nextCursor" path: "meta.next"}]
      }
    )
```

The above example illustrates some important aspects. The `after` parameter is set to an empty string, which indicates that it is the first request. In contrast, the first parameter is set to `20`, meaning that 20 results will be returned per page. Remember, the value of the first parameter remains the same for subsequent requests as well. The `after` parameter in the upcoming request will be according to the page number you want to be returned. For instance, it is `3` for the third and `4` for the fourth page.

### 3\. Implementing Cursor Pagination

Implementing cursor-based pagination is very similar to the other two pagination methods. The only difference is that you need to specify the `type` parameter as `NEXT_CURSOR`, and change the setters parameter to point towards the field that indicates the next cursor.

```graphql
customers(
  first: Int! = 20 
  after: String! = ""
): CustomerConnection
  @rest(
    endpoint:"https://api.example.com/customers?first=$first&after=$after"
    pagination: {
        type: NEXT_CURSOR
        setters: [{field:"nextCursor" path: "meta.next"}]
      }
    )
```

This, of course, only applies if your REST API already supports cursor-based pagination.

How to Query Cursor Pagination in GraphQL
-----------------------------------------

In the previous section, you've learned how to implement cursor-based pagination for any REST API using StepZen. Cursor-based pagination is for example, used by [Relay](https://relay.dev/graphql/connections.htm), and is also supported by the StepZen GraphQL API. Cursor-based pagination is the preferred way to do pagination in GraphQL, as it provides valuable data for UX uses. But it comes with the downside of being more complex to query, as we saw in the first section.

Let's look at the `Connection` type that is used by cursor-based pagination:

```graphql
type Customer {
  activities: [Activity]
  addresses: [Address]
  contacts: Contacts
  description: String
  designation: String
}

type CustomerEdge {
  node: Customer
  cursor: String
}

type CustomerConnection {
  pageInfo: PageInfo!
  edges: [CustomerEdge]
}
```

The `CustomerConnection` type is the one that is returned by the `customers` query. It contains a `pageInfo` field, which is of type `PageInfo`. The `PageInfo` type contains the following fields:

```graphql
query MyQuery {
  customers {
    pageInfo {
      endCursor
      hasNextPage
      hasPreviousPage
      startCursor
    }
  }
}
```

These fields inform you about the current page, and whether there are more pages to be fetched. The `endCursor` and `startCursor` fields are the cursors that you need to use in the next request. The `hasNextPage` and `hasPreviousPage` fields indicate whether there are more pages to be fetched.

The `edges` field contains a list of `CustomerEdge` objects. The `CustomerEdge` type contains a `node` field of type `Customer`. To get the information about the customer, you would need to use the `node` field:

```graphql
query MyQuery {
  customers(first: 3, after: "eyJjIjoiTzpRdWVyeTpwYXJrcyIsIm8iOjl9") {
    edges {
      node {
        id
        description
      }
    }
  }
}
```

The concept of "edges and nodes" is a bit confusing but very straightforward. It comes from the concept that everything in GraphQL is a graph. The `edges` field contains a list of `CustomerEdge` objects, information that is specific for this connection but not shared by all nodes. The `CustomerEdge` type contains a `node` field of type `Customer`. This is the actual customer information.

![Connections, Edges, and Nodes in GraphQL](https://stepzen.com/images/blog/graphql-connections-edges-nodes.png)

For Relay, you can find more information on GraphQL connections in the [documentation](https://relay.dev/graphql/connections.htm).

Conclusion
----------

GraphQL allows different kinds of pagination. But how does one decide which one will work best? GraphQL recommends using cursor-based pagination. However, it depends on the requirements of your application. If you are using a REST API already supporting cursor-based pagination, then it's a no-brainer. But if you are using a REST API that is not supporting cursor-based pagination, then you can use StepZen to implement it.

The common and the most straightforward kinds of pagination are **offset** and **page number**. Although they are relatively simple to implement, they are more suitable for static data. In contrast, **cursor-based pagination** is complex but best for changing data as it prevents data inconsistencies. With StepZen, it's straightforward to implement cursor-based pagination for any API.

Learn more by visiting [StepZen Docs](https://stepzen.com/docs/quick-start). Try it out with a [free account](https://stepzen.com/signup), and we'd love to get your feedback and answer any questions on our [Discord](https://discord.com/invite/9k2VdPn2FR).