Grafbase exposes a Relay-style Pagination API that you can use outside of Relay with other client libraries, such as Apollo Client.
Apollo Client is a JavaScript library that helps developers manage data in a client-side application. It is designed to work with GraphQL, and provides an easy-to-use DX for fetching and caching data.
Pagination is used to split large sets of data into smaller, more manageable subsets often referred to as "pages".
Using pagination can lead to greater performance for the end user as pages and databases have less data to load. Pagination also has SEO benefits as faster load times lead to greater user experience which most search engines rank higher over those that don't.
Offset-based pagination requires you to provide an offset (or page number) and a limit (or page size) to instruct the database which subset of data should be fetched. This approach is often slower for large datasets but it's often the easiest to implement.
Cursor-based pagination however is the better and safer option. This approach requires you to provide a "cursor" to indicate the position in the dataset that results should be retrieved. Each page of data includes a cursor that points to the next set of results which allowing the database to work more efficiently, and scales as your data grows.
Thankfully Grafbase follows the Relay specification for pagination and tools like Apollo Client make this easy with a utility that can attach to any query to correctly handle the caching of cursor-based paginated results.
Let's begin by creating a GraphQL backend using the Grafbase CLI.
In the root of your existing React + Apollo application run the following:
npx grafbase init --template https://github.com/grafbase/grafbase/tree/main/examples/react-apollo-relay-pagination
Then open the generated grafbase/schema.graphql
file. You should see the following schema that defines your backend:
type Post @model {
title: String!
}
We can use the Grafbase CLI to run the GraphQL backend locally:
npx grafbase dev
You should see a success message that your GraphQL Playground and API is running at http://localhost:4000
.
In this guide we'll only explore querying data from our backend, so you will need to populate the backend using a GraphQL mutation inside the playground.
mutation {
postCreate(input: { title: "Hello Grafbase!" }) {
post {
id
}
}
}
You'll want to make sure you install @apollo/client
and graphql
inside of your project if you are not already using Apollo Client:
npm install @apollo/client graphql
Now you will need to create a client
:
import { ApolloClient, InMemoryCache } from '@apollo/client'
const client = new ApolloClient({
uri: '<YOUR_GRAFBASE_API_URL>',
cache: new InMemoryCache(),
})
Now all that's left to do is wrap your application with ApolloProvider
:
<ApolloProvider client={client}>
<App />
</ApolloProvider>
Note: YOUR_GRAFBASE_API_URL
when using the CLI will be http://localhost:4000/graphql
Where you have configured the ApolloClient
instance is where you will need to update the typePolicies
for the collection query.
In this example we use the postCollection
and update the InMemoryChace
to use relayStylePagination()
utility:
import { relayStylePagination } from '@apollo/client/utilities'
const client = new ApolloClient({
uri: '<YOUR_GRAFBASE_API_URL>',
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
postCollection: relayStylePagination(),
},
},
},
}),
})
We will now use GraphQL variables for the cursor
and pass it into the collection query arguments:
const POSTS_QUERY = gql`
query GetPostsAfterCursor($cursor: String) {
postCollection(first: 1, after: $cursor) {
edges {
node {
id
title
}
}
pageInfo {
endCursor
hasNextPage
}
}
}
`
Notice above we are also fetching the pageInfo
which includes the endCursor
value we need to pass to our query, and the hasNextPage
boolean. We can use this boolean to toggle displaying a load more
button.
Now we have the query above all that's left to do is update our application to fetchMore
when we click a button.
Where you invoke useQuery
you will want to destructure fetchMore
:
const { data, loading, fetchMore } = useQuery(POSTS_QUERY)
Now when you want to "fetch more" all you need to do is invoke that function and password the "end cursor
" value. Here's a full look at the <App />
component that renders a list of posts with a <button>
to "load more":
export default function App() {
const { data, loading, fetchMore } = useQuery(POSTS_QUERY)
if (loading) return <p>Loading posts...</p>
return (
<div>
<ul>
{data.postCollection?.edges?.nodes.map(({ node: { id, title } }) => (
<li key={id}>{title}</li>
))}
</ul>
{data.postCollection?.pageInfo?.hasNextPage && (
<button
onClick={() =>
fetchMore({
variables: {
cursor: pageInfo.endCursor,
},
})
}
>
Load more
</button>
)}
</div>
)
}
Here are some other guides you might find interesting when working with Grafbase and Apollo Client: