Throughout the Propel API, you’ll encounter collections of items: lists of Applications, Data Sources, Data Pools, and Metrics, for example. Rather than give you every item in the collection — which could be prohibitively large — the Propel API returns them to you in parts called “pages”.

The Propel GraphQL APIs uses “cursors” for accessing these pages: when fetching a collection, the initial set of results arrives in a page containing “forward” and “backward” cursors. You can access the next page of results by following the forward cursor, and you can access the previous page of results by following the backward cursor. This is how user interfaces featuring “next page” and “previous page” buttons are typically implemented.

This guide explains the mechanics of cursor-based pagination and how Propel GraphQL APIs use it.

Cursor-based pagination overview

Cursor-based pagination works by requesting some number of items before or after a particular item (the cursor) in a collection. The API returns the fetched items and a new pair of cursors pointing to the first and last items of the results. Your application can then use these new cursors in subsequent requests to page forward or backward through the items, always fetching the next page before or the next item after the first and last items in the current page, respectively.

Key terminology

  • Your application - The web or mobile application you are building.
  • Cursor - A pointer to an individual item in a collection.
  1. Your application makes an initial request with a parameter indicating the number of items it wants per page, 10, for example.
  2. The Propel API responds with the items if they exist and a pagination object (PageInfo) that contains a cursor to the first and last item of the results and whether a next or previous page exists.
  3. To see the next items, your application makes another API request indicating the number of items to fetch and the cursor to the next page.
  4. The Propel API fetches the next 10 items after the cursor and returns them to your application.

The request parameters

The Propel API expects the following parameters when making requests that fetch collections of items in list-style queries in GraphQL (dataSources, dataPools, metrics, and applications).

To page forward, the first and after parameters are expected:

ParameterDescriptionDefaultMaximunRequired
firstIndicates the number of items to be returned.10100No
afterThe cursor that indicates the position where to start fetching items forward. The result will not include the item in the after cursor.most recentNo

To page backward, the last and before parameters are expected:

ParameterDescriptionDefaultMaximunRequired
lastIndicates the number of items to be returned.10100No
beforeThe cursor that indicates the position where to start fetching items backward. The result will not include the item in the before cursor.most recentNo

All parameters are optional. Below is the Propel API behavior for each case.

  • If no parameters are provided, the most recent 10 items will be returned.
  • If all parameters are provided, a 400 Bad Request is returned.
  • If only first is provided, the most recent specified number of items will be returned.
  • If only last is provided, an empty list is returned.
  • If only after or before are provided, the default count of items before or after the cursor will be returned.
  • If both after and before are provided, a 400 Bad Request is returned.
  • If only first and before are provided, a 400 Bad Request is returned.
  • If only last and after are provided, a 400 Bad Request is returned.

The PageInfo response object

Every API response to requests fetching collections of items like list queries in GraphQL (dataSources, dataPools, metrics, and applications) will return a PageInfo object.

The PageInfo object has the following properties:

  • startCursor - Points to the first item returned in the results. Used when paginating backward.
  • endCursor - Points to the last item returned in the results. Used when paginating forward.
  • hasNextPage - A boolean that indicates whether a next page exist. Used to know whether to display a “Next page” UI button.
  • hasPreviousPage - A boolean that indicates whether a previous page exist. Used to know whether to display a “Previous page” UI button.

A note about cursors

Cursors are opaque strings that contain the necessary information to locate the next (or previous) page of results. Your application doesn’t need to know the format of the cursor — just pass the cursor string along to Propel, and we’ll figure out the rest.

GraphQL API pagination

Propel’s GraphQL cursor-based pagination is based on the Relay Cursor Connections Specification. Apollo has a great in-depth post, “Explaining GraphQL Connections”, that we recommend if the concept of GraphQL connections is new to you.

The key thing to note is that, in addition to the parameters and pageInfo response object described above, GraphQL cursor-based pagination makes use of Connections and Edges.

  • Connections - A wrapper object that contains the details of the list you are paging through. A connection has two properties: edges and pageInfo. The property pageInfo is an instance of the PageInfo object described above.

  • Edge - A wrapper around an individual item in the result set that includes the item and a cursor that points to it.

The wrapper objects are a standardized way to include the cursor value when returning an object and the pageInfo object when returning the list without changing the base objects.

Let’s look at an example of how these objects look like in the Metrics GraphQL Schema.

A Metric connection has the pageInfo object and a MetricEdge array.

type MetricConnection {
  pageInfo: PageInfo!
  edges: [MetricEdge!]
}

The Metric Edge has the cursor and the actual Metric object.

type MetricEdge {
  cursor: String!
  node: Metric!
}

In its simplest invocation, the getAllMetrics query would be called with either first and after or last and before and would return a MetricsConnection.

getAllMetrics(
		first: Int
		after: String
		last: Int
		before: String
		): MetricsConnection

Example getAllMetrics request

This example illustrates how to fetch the 50 most recently created Metrics across two pages of 25 Metrics each.

First we fetch the 25 most recently created Metrics, setting first to 25 in our query. This will return the first page of results. Make sure to request pageInfo and edges to be included in the response.

Query getAllMetrics {
	getAllMetrics(first: 25){
        pageInfo
        edges
    }
}

The response arrives as a MetricConnection object representing the first page of results:

{
  "data":{
    "metrics": {
        "pageInfo": {
            "startCursor": "YWNjb3VudElkOkFDVDAxQVJaM05ERUtUU1Y0UlJGRlE2OUc1RkFW",
            "endCursor": "YWNjb3VudElkOkFDVDAxQVJaM05ERUtUU1Y0UlJGRlE2OUc1RllI",
            "hasNextPage": true,
            "hasPreviousPage": false
        },
        "edges": [{
            "cursor":"YWNjb3VudElkOkFDVDAxQVJaM05ERUtUU1Y0UlJGRlE2OUc1RkFW",
            "node": {
                "id": "MET01ARZ3NDEKTSV4RRFFQ69G5FAV",
                ... all other Metric properties ...
            }
        },
        ... all other 23 edges ..
        {
            "cursor":"YWNjb3VudElkOkFDVDAxQVJaM05ERUtUU1Y0UlJGRlE2OUc1RllI",
            "node": {
                "id": "MET04AZ3NDEKTSV4R5FAVRRFFQ69G",
                ... all other Metric properties ...
            }
        }]
    }
}

Now, we want to fetch the next 25 Metrics. To do so, we make another query setting first to 25 and after to the value of the endCursor we got in the MetricsConnection response. This will return the second page of results.

Query metrics {
	getAllMetrics(first: 25, after: 'YWNjb3VudElkOkFDVDAxQVJaM05ERUtUU1Y0UlJGRlE2OUc1RllI'){
        pageInfo
        edges
    }
}

This will return the next 25 metrics if they exist.

Happy pagination!