How to Query Paginated Data in GraphQL
The Profile GraphQL API uses Relay-style cursor-based connections to support efficient and flexible querying of large lists like Transactions.
Relay-style connections are similar to basic cursor-based pagination, but have a slightly different response structure:
count
returns the total number of items in the list.edges
returns an array ofcursor
andnode
objects, where eachnode
is an item in the list.pageInfo
returns an object containing pagination metadata, includingstartCursor
,endCursor
,hasPreviousPage
andhasNextPage
fields to help paginate subsequent queries.
Link to this section#Paginating a Profile's Transactions
We can construct a query to return a paginated list of the Profile's Transactions:
query GetTransactions($after: String) {
transactionsConnection(after: $after) {
count
edges {
cursor
node {
id
description
amount
date
}
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
Link to this section#Variables
{
"after": null
}
Link to this section#Response
{
"data": {
"transactionsConnection": {
"count": 555,
"edges": [
{
"cursor": "MQ",
"node": {
"id": "txn_1vxuyF6nj8O4RnunE1ndqu",
"description": "Chipotle Mexican Grill",
"amount": -25,
"date": "2022-04-05",
},
...
},
...
]
},
"pageInfo": {
"startCursor": "MQ",
"endCursor": "Mw",
"hasNextPage": true,
"hasPreviousPage": true
}
}
}
The response will return all Transaction nodes with cursors from MQ
to Mw
. To get the next page, we repeat this query setting the value of after
to the value of pageInfo.endCursor
.
{
"after": "Mw"
}
Once pageInfo.hasNextPage
returns false
, we've completed all the available transactions.
Link to this section#Using Pagination with React + Apollo
In this example, we use the React Apollo Client to create a TransactionsList React component that renders a paginated list of the user's transactions.
import * as React from 'react'
import { gql, useQuery } from '@apollo/client'
const Transactions = () => {
// Construct the query
const TRANSACTIONS_QUERY = gql`
query Transactions($after: String, $first: Int) {
transactionsConnection(sort: DATE_DESC, first: $first, after: $after) {
count
edges {
cursor
node {
id
description
amount
date
}
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
`
// Initialize variables for first query
const first = 10
const after = undefined
// Set up the Apollo `useQuery` hook
const { data, error, loading, fetchMore } = useQuery(TRANSACTIONS_QUERY, {
variables: { after: after, first: first },
})
// Handle errors
if (error) {
console.log('Something went wrong', error)
}
// Display a message while the message is in transit
if (loading) {
return <>Loading...</>
}
const { transactionsConnection } = data
const { edges, count, pageInfo } = transactionsConnection
const { hasNextPage, endCursor } = pageInfo
if (count === 0) {
return <>No transactions found</>
}
// Fetch more data on button click
const handleLoadMore = () => {
fetchMore({
variables: { after: endCursor, first: first },
})
}
return (
<div>
<p>{`${count} Transactions`}</p>
<ul>
{edges.map((transaction) => {
const { node, cursor } = transaction
const { description, amount, date, status } = node
// Return a single transaction as a list item
return (
<li key={cursor}>
<div>
<p>{description}</p>
<p>{date}</p>
<p>{status}</p>
<p>{amount}</p>
</div>
</li>
)
})}
</ul>
{/* Only show "Load More" button if "hasNextPage" is true and more transactions are expected */}
{hasNextPage && (
<button type="button" onClick={handleLoadMore}>
Load More
</button>
)}
</div>
)
}
export default Transactions