How to break GitHub GraphQL Pagination

~ 3 minutes read
no time? jump straight to conclusion

GraphQL is a query language for APIs. GitHubs API powers graphql with a live graphql API (as well as REST). A common use case in graphql is traversing the relationship between sets of objects known as pagination. GitHub uses a complete connection model for pagination.
This post describes an issue with GitHubs graphql pagination and how to work around it.

How to

One use case for pagination is to retrieve a list of repositories per organization. Such a graphql retrieval request can consist of a query and query variables.

Query:

query ($org: String!, $after: String, $createdat: String!, $order: String!) {
  organization(login: $org) {
    description
    repositories(first: 2, after: $after, orderBy: {field: $createdat, direction: $order}) {
      nodes {
        url
      }
      pageInfo {
        endCursor
        hasNextPage
        hasPreviousPage
      }
      totalCount
    }
  }
}

Query variables:

{
  "org": "[your_org]",
  "after": null,
  "createdat": "CREATED_AT",	
  "order": "ASC"
}

The result includes:

...
        "pageInfo": {
          "endCursor": "Y3Vyc29yOnYyOpLAzgD-FL4=",
          "hasNextPage": true,
          "hasPreviousPage": false
        },
...

The next page gets called with adapted query variables:

{
  "org": "[your_org]",
  "after": "Y3Vyc29yOnYyOpLAzgD-FL4=",
  "createdat": "CREATED_AT",	
  "order": "ASC"
}

This results includes:

...
      "repositories": {
        "nodes": [],
        "pageInfo": {
          "endCursor": null,
          "hasNextPage": false,
          "hasPreviousPage": false
        },
...

The repository request includes an order direction:

orderBy: {field: $createdat, direction: $order}

This order direction broke the pagination. Although there are more repositories on GitHub for the specific organization I tested that with, the repositories are not listed in the result payload.

The Fix

Remove the order directive to workaround the pagination issue:

orderBy: {field: $createdat, direction: $order}

Now the query looks like this:

query ($org: String!, $after: String) {
  organization(login: $org) {
    description
    repositories(first: 2, after: $after) {
      nodes {
        url
      }
      pageInfo {
        endCursor
        hasNextPage
        hasPreviousPage
      }
      totalCount
    }
  }
}

Query variables:

{
  "org": "[your_org]",
  "after": null
}

The result includes:

        "pageInfo": {
          "endCursor": "Y3Vyc29yOnYyOpHOAP4Uvg==",
          "hasNextPage": true,
          "hasPreviousPage": false
        },

The next page gets called with adapted query variables:

{
  "org": "[your_org]",
  "after": "Y3Vyc29yOnYyOpHOAP4Uvg=="
}

The result looks similar to this

{
  "data": {
    "organization": {
      "description": "from A to Beyond",
      "repositories": {
        "nodes": [
          {
            "url": "https://github.com/[your_org]/[repo_3]"
          },
          {
            "url": "https://github.com/[your_org]/[repo_4]"
          }
        ],
        "pageInfo": {
          "endCursor": "Y3Vyc29yOnYyOpHOARso4Q==",
          "hasNextPage": true,
          "hasPreviousPage": true
        },
        "totalCount": [some number]
      }
    }
  }
}

You would continue to use the endCursor value of the response payload in the subsequent query variables until

"hasNextPage": false

Conclusion

Ordering repository lists with an order directive

orderBy: {field: $createdat, direction: $order} 

as shown above breaks pagination in the GitHub GraphQL API.
That is also true for ordering only by creation date or direction.
I reported the described behaviour to GitHub support and will update this blog post once I learned more.
Make sure you do not apply an order directive like this to avoid broken pagination.

Be the first to comment

Leave a Reply

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.