GraphQL Codegen with React Query
In this guide, we will learn how to use the GraphQL Code Generator client preset with React Query to generate type-safe operations and wire it up to a GraphQL server that supports the GraphQL over HTTP protocol.
Prerequisites
For this guide, we assume that you are querying your GraphQL server from within a Browser environment. So you already have your Vite, Next.js, Node.js or any other vanilla project with TypeScript setup.
We are going to use the public Star Wars GraphQL API as our GraphQL endpoint.
Setting up GraphQL Code Generator
The GraphQL Code Generator client preset is the preferred and built-in way to generate type-safe operations for any GraphQL client library and also vanilla JavaScript.
To get started, install the following dependencies
@graphql-codegen/cli
: Codegen CLI for running code generation@parcel/watcher
: Enable watch mode for the codegen CLI@graphql-codegen/schema-ast
: Plugin for generating the schema file from the GraphQL API endpoint (optional if you already have a schema file)@0no-co/graphqlsp
: TypeScript language server plugin for GraphQL auto-complete (optional)
Feel free to omit the optional dependencies if you don’t need them.
npm install --save-dev @graphql-codegen/cli @parcel/watcher
npm install --save-dev @graphql-codegen/schema-ast
npm install --save-dev @0no-co/graphqlsp
After that, we can create a codegen.ts
file in the root of our project with the following
contents:
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
schema: 'https://swapi-graphql.netlify.app/.netlify/functions/index',
documents: ['src/**/*.tsx'],
ignoreNoDocuments: true,
generates: {
'./src/graphql/': {
preset: 'client',
config: {
documentMode: 'string'
}
},
'./schema.graphql': {
plugins: ['schema-ast'],
config: {
includeDirectives: true
}
}
}
}
export default config
Next, we adjust our tsconfig.json
to load @0no-co/graphqlsp
.
{
"compilerOptions": {
"plugins": [
{
"name": "@0no-co/graphqlsp",
"schema": "./schema.graphql"
}
]
}
}
Finally, we also need to prompt Visual Studio Code to use the local TypeScript version by creating a
.vscode/settings.json
file with the following contents:
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
Running the Code Generator
Now we can run the following command to generate the schema.graphql
file and the GraphQL Code
Generator client code. Note: We are not yet writing GraphQL operations in our codebase, we just
generate the client boilerplate code.
npx graphql-codegen --config codegen.ts
After running the command, you should see a new schema.graphql
file in the root of your project
and a new folder src/graphql
with the generated client code.
You almost never need to touch the files within src/graphql
as they are generated and overwritten
by GraphQL Code generator.
We will now use the generated client code to write our type-safe GraphQL operations.
Writing GraphQL Operations
Let’s start GraphQL Code Generator in watch mode to generate the client code whenever we write our code.
npx graphql-codegen --config codegen.ts --watch
Next within any file in our projects src
folder, we will import the graphql
function from within
src/graphql
.
import { graphql } from './graphql'
This function allows us to define a GraphQL operation.
Thanks to the TypeScript GraphQL LSP plugin, we get auto-complete for our GraphQL operations while writing them.
With that, we will write a simple query operation to get the total count of people in the Star Wars universe.
import { graphql } from './graphql'
const PeopleCountQuery = graphql(`
query PeopleCount {
allPeople {
totalCount
}
}
`)
As we now save the file in our editor, the GraphQL Code Generator will generate the corresponding
types, and as you hover over the PeopleCountQuery
variable, you will see the following:
TypedDocumentString
is a container type that holds the query operation string and also the
TypeScript type for that operations response.
We can now leverage this to build a type-safe function that executes the GraphQL operation against our GraphQL server.
Type-Safe GraphQL Operation Execution
We can build a simple wrapper around fetch
that takes a TypedDocumentString
parameter and
returns a typed response.
import type { TypedDocumentString } from './graphql'
export async function execute<TResult, TVariables>(
query: TypedDocumentString<TResult, TVariables>,
...[variables]: TVariables extends Record<string, never> ? [] : [TVariables]
) {
const response = await fetch('https://swapi-graphql.netlify.app/.netlify/functions/index', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/graphql-response+json'
},
body: JSON.stringify({
query: document,
variables
})
})
if (!response.ok) {
throw new Error('Network response was not ok')
}
return response.json() as TResult
}
We can now use this function to execute our PeopleCountQuery
operation.
import { graphql } from './graphql'
import { execute } from './graphql/execute'
const PeopleCountQuery = graphql(`
query PeopleCount {
allPeople {
totalCount
}
}
`)
execute(PeopleCountQuery).then(data => {
console.log(data)
})
When we now hover over the data
parameter in the then
callback, we can see that the response is
fully typed.
Executing Query Operations with React Query
We can now leverage the execute
function to execute our GraphQL operations with React Query.
import { useQuery } from '@tanstack/react-query'
import { graphql } from './graphql'
import { execute } from './graphql/execute'
const PeopleCountQuery = graphql(`
query PeopleCount {
allPeople {
totalCount
}
}
`)
function App() {
const { data } = useQuery({
queryKey: ['films'],
queryFn: () => execute(PeopleCountQuery)
})
return <div>There are {data?.allPeople.totalCount} people</div>
}
Conclusion
In this article, we learned how to use GraphQL Code Generator preset with React Query.
If you want to learn more about GraphQL Code Generator, check out the client preset documentation. E.g. you want to reduce bundle size by using the client preset babel plugin or enable persisted documents in production for security and performance reasons.