Skip to main content

Paginating results with GraphQL

When you use a connection to retrieve a list of resources, you use arguments to specify the number of results to retrieve. You can select which set of results to retrieve from a connection by using cursor-based pagination.

Note

You can retrieve up to a maximum of 250 resources. If you need to paginate larger volumes of data, then you can perform a bulk query operation using the GraphQL Admin API.


Connections retrieve a list of nodes. A node is an object that has a global ID and is of a type that's defined by the schema, such as the Order type. For example, the orders connection finds all the Order nodes connected to the query root. The nodes field is similar to a for-loop because it retrieves the selected fields from each node in the connection.

To optimize performance and user experience, you can request only a certain number of nodes at a time. The batch of nodes that is returned is known as a page. The position of each node in the array is indicated by its cursor.

To retrieve the next page of nodes, you need to indicate the position of the node the page should start from. You can do so by providing a cursor. You can retrieve cursor information about the current page using the PageInfo object, and use that cursor value in a subsequent query by passing it in a after or before argument.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query {
orders(first: 2) {
nodes {
id
name
createdAt
}
}
}

JSON response

{
"data": {
"orders": {
"nodes": [
{
"id": "gid://shopify/Order/1",
"name": "#1001",
"createdAt": "2022-05-12T19:42:48Z"
},
{
"id": "gid://shopify/Order/2",
"name": "#1002",
"createdAt": "2022-05-12T19:45:07Z"
}
]
}
}
}
Tip

You can also retrieve a list of nodes using edges.

In the GraphQL Admin API, each connection returns a PageInfo object that assists in cursor-based pagination. The PageInfo object is composed of the following fields:

FieldTypeDescription
hasPreviousPageBooleanWhether there are results in the connection before the current page.
hasNextPageBooleanWhether there are results in the connection after the current page.
startCursorstringThe cursor of the first node in the nodes list.
endCursorstringThe cursor of the last node in the nodes list.
Note

The PageInfo object in the GraphQL Partner API is only composed of the hasNextPage and hasPreviousPage fields.

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query {
orders(first: 2) {
nodes {
id
name
createdAt
}
pageInfo {
hasPreviousPage
hasNextPage
startCursor
endCursor
}
}
}

JSON response

{
"data": {
"orders": {
"nodes": [
{
"id": "gid://shopify/Order/1",
"name": "#1001",
"createdAt": "2022-05-12T19:42:48Z"
},
{
"id": "gid://shopify/Order/2",
"name": "#1002",
"createdAt": "2022-05-12T19:45:07Z"
}
]
"pageInfo": {
"hasPreviousPage": false,
"hasNextPage": true,
"startCursor": "eyJsYXN0X2lkIjoxNDIzOTgwNTI3NjM4LCJsYXN0X3ZhbHVlIjoiMjAyMC0wMS0yMCAxNDo0ODoxMS4wMDAwMDAifQ==",
"endCursor": "eyJsYXN0X2lkIjoyMzIxMjM5MTQ2NTE4LCJsYXN0X3ZhbHVlIjoiMjAyMC0xMi0xNSAyMzowMDo0NS4wMDAwMDAifQ=="
}
}
}
}

All connections in Shopify's APIs provide forward pagination. This is achieved with the following connection variables:

FieldTypeDescription
firstintegerThe requested number of nodes for each page.
afterstringThe cursor to retrieve nodes after in the connection. Typically, you should pass the endCursor of the previous page as after.

You can include the PageInfo fields in your queries to paginate your results. The following example includes the hasNextPage and endCursor fields, and uses query variables to pass the endCursor value as an argument:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query ($numProducts: Int!, $cursor: String) {
# The `$numProducts` variable is required and is used to specify the number of results to return. The `$cursor` variable isn't required. If the `$cursor` variable is omitted, then the `after` argument is ignored.
products(first: $numProducts, after: $cursor) {
nodes {
title
}
pageInfo {
hasNextPage
endCursor
}
}
}

Variables

{
"numProducts": 3,
"cursor": null
}

JSON response

{
"data": {
"products": {
"nodes": [
{
"title": "Product 1 title"
},
{
"title": "Product 2 title"
},
{
"title": "Product 3 title"
}
],
"pageInfo": {
// The response indicates that there's a next page and provides the cursor to use as an `after` input for the next page of nodes.
"hasNextPage": true,
"endCursor": "eyJsYXN0X2lkIjo3MDE3MjQ0MTY0MTUyLCJsYXN0X3ZhbHVlIjoiNzAxNzI0NDE2NDE1MiJ9"
}
}
}
}

By using the same query with different variables, you can query for the next page:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query ($numProducts: Int!, $cursor: String){
products(first: $numProducts, after: $cursor) {
nodes {
title
}
pageInfo {
hasNextPage
endCursor
}
}
}

Variables

{
"numProducts": 3,
"cursor": "eyJsYXN0X2lkIjo3MDE3MjQ0MTY0MTUyLCJsYXN0X3ZhbHVlIjoiNzAxNzI0NDE2NDE1MiJ9"
}

JSON response

{
"data": {
"products": {
"nodes": [
{
"title": "Product 4 title"
}
],
"pageInfo": {
// The response indicates that there's no next page. This is the last page of the connection.
"hasNextPage": false,
"endCursor": "eyJsYXN0X2lkIjo3MjE0Njc0MjgwNTA0LCJsYXN0X3ZhbHVlIjoiNzIxNDY3NDI4MDUwNCJ9"
}
}
}
}

Some connections in Shopify's APIs also provide backward pagination. This is achieved with the following connection variables:

FieldTypeDescription
lastintegerThe requested number of nodes for each page.
beforestringThe cursor to retrieve nodes before in the connection. Typically, you should pass the startCursor of the previous page as before.

Similar to forward pagination, you can start at the end of the list of nodes, and then query in reverse page order to the beginning. The following example includes the hasPreviousPage and startCursor fields, and uses query variables to pass the startCursor value as an argument:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query ($numProducts: Int!, $cursor: String){
products(last: $numProducts, before: $cursor) {
nodes {
title
}
pageInfo {
hasPreviousPage
startCursor
}
}
}

Variables

{
"numProducts": 3,
"cursor": null
}

JSON response

{
"data": {
"products": {
"nodes": [
{
"title": "Product 2 title"
},
{
"title": "Product 3 title"
},
{
"title": "Product 4 title"
}
],
"pageInfo": {
"hasPreviousPage": true,
"startCursor": "eyJsYXN0X2lkIjo3MDE3MjQ0MDk4NjE2LCJsYXN0X3ZhbHVlIjoiNzAxNzI0NDA5ODYxNiJ9"
}
}
}
}

The startCursor field can also be used in the subsequent request as the input before to get the previous page:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query ($numProducts: Int!, $cursor: String){
products(last: $numProducts, before: $cursor) {
nodes {
title
}
pageInfo {
hasPreviousPage
startCursor
}
}
}

Variables

{
"numProducts": 3,
"cursor": "eyJsYXN0X2lkIjo3MDE3MjQ0MDk4NjE2LCJsYXN0X3ZhbHVlIjoiNzAxNzI0NDA5ODYxNiJ9"
}

JSON response

{
"data": {
"products": {
"nodes": [
{
"title": "Product 1 title"
}
],
"pageInfo": {
"hasPreviousPage": false,
"startCursor": "eyJsYXN0X2lkIjo3MDE3MjQ0MDY1ODQ4LCJsYXN0X3ZhbHVlIjoiNzAxNzI0NDA2NTg0OCJ9"
}
}
}
}

In connections, an Edge type describes the connection between the node and its parent. In almost all cases, querying nodes and pageInfo is preferred to querying edges. However, if you want the Edge metadata, then you can query edges instead of nodes. Each Edge contains a minimum of that edge's cursor and the node.

The following query is equivalent to the forward pagination query. However, it requests a cursor for every edge instead of only the endCursor:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

GraphQL query

query ($numProducts: Int!, $cursor: String){
products(first: $numProducts, after: $cursor) {
edges {
cursor
node {
title
}
}
pageInfo {
hasNextPage
endCursor
}
}
}

Variables

{
"numProducts": 3,
"cursor": null
}

JSON response

// The PageInfo `endCursor` and the last edge's `cursor` are the same. Also, the `edges[].node` list is the equivalent of the `nodes` list in the forward pagination query.
{
"data": {
"products": {
"edges": [
{
"cursor": "eyJsYXN0X2lkIjo3MDE3MjQ0MDY1ODQ4LCJsYXN0X3ZhbHVlIjoiNzAxNzI0NDA2NTg0OCJ9",
"node": {
"title": "Product 1 title"
}
},
{
"cursor": "eyJsYXN0X2lkIjo3MDE3MjQ0MDk4NjE2LCJsYXN0X3ZhbHVlIjoiNzAxNzI0NDA5ODYxNiJ9",
"node": {
"title": "Product 2 title"
}
},
{
"cursor": "eyJsYXN0X2lkIjo3MDE3MjQ0MTY0MTUyLCJsYXN0X3ZhbHVlIjoiNzAxNzI0NDE2NDE1MiJ9",
"node": {
"title": "Product 3 title"
}
}
],
"pageInfo": {
"hasNextPage": true,
"endCursor": "eyJsYXN0X2lkIjo3MDE3MjQ0MTY0MTUyLCJsYXN0X3ZhbHVlIjoiNzAxNzI0NDE2NDE1MiJ9"
}
}
}
}

Anchor to Search performance considerationsSearch performance considerations

Paginating resources using a range search might timeout or return an error if the collection of resources is sufficiently large, and the search field is different from the specified (or default) sort key for the connection you are querying. If your query is slow or returns an error, then try specifying a sort key that matches the field used in the search. For example:

POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

{
orders(first: 250, query: "created_at:>'2020-10-21'", sortKey: CREATED_AT) {
edges {
node {
id
}
}
}
}

Was this page helpful?