post picture
How to use GraphQL Code Generator with React Query
Published: 19 July 2022

GraphQL is a great way to build apps faster. But, if you're only using GraphQL without a GraphQL Code Generator (codegen) you're missing out on the full potential of GraphQL.

In this blog post, you'll learn how to use the missing piece (GraphQL Code Generator) to unleash the full potential of GraphQL with React and React Query.

Let's get started.

Setup

Three simple steps are required to set up GraphQL Code Generator:

  1. Install a few npm packages.

  2. Create a GraphQL Code Generator configuration file.

  3. Add a GraphQL Code Generator script to the package.json file.

Install NPM packages

Install the following npm packages required to run GraphQL Code Generator. These packages will help us:

  • Generate types for TypeScript

  • Generate hooks and helper functions for React and React Query.

npm install --dev @graphql-codegen/cli @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-query graphql
# or yarn
yarn add -D @graphql-codegen/cli @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-query graphql
# or pnpm
pnpm add -D @graphql-codegen/cli @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-query graphql

Create a Config File for GraphQL Code Generator

Next, create a graphql.config.yaml file in the root of your git repo.

This configuration file contains information about:

  • Your GraphQL API.

  • Where to find GraphQL queries and mutations in your code.

  • What types and hooks to generate.

Here's the configuration file. Update the file with your GraphQL endpoint. If you're using Nhost and Hasura you also need to add the admin secret so the GraphQL Code Generator can read your whole GraphQL API.

schema:
  - http://localhost:1337/v1/graphql:
      headers:
        x-hasura-admin-secret: nhost-admin-secret
generates:
  src/utils/__generated__/graphql.ts:
    documents:
      - 'src/**/*.graphql'
    plugins:
      - 'typescript'
      - 'typescript-operations'
      - 'typescript-react-query'
    config:
      fetcher:
        func: '../graphql-fetcher#fetchData'
        isReactHook: false

Add a Codegen Script to package.json

As the last step, let's add a script to the package.json to run GraphQL Code Generator.

{
  /*...*/
  "scripts": {
    /*...*/
    "codegen": "graphql-codegen --config graphql.config.yaml --errors-only"
  }
  /*...*/
}

You'll run this script (npm run codegen) every time you've changed anything in your GraphQL API or in your GraphQL files to get the most up-to-date types generated.

You've now completed the three configuration steps. It's time to start using the GraphQL Code Generator and unleash its power!

Workflow

As a mental model, this is how you will use the GraphQL Code Generator in your development workflow:

  1. Manage GraphQL queries and mutations in .graphql files

  2. Run npm run codegen to generate types and hooks for queries, mutations, and subscriptions.

  3. Use the hooks and types in your React app.

Example

We'll go through two examples of how to use your new GraphQL Code Generator setup. One is using a query to fetch data and one is using a mutation to modify data.

For the two examples, we're using a table customers in Hasura with two columns:

  • id - UUID

  • name - Text

Go ahead and create the customers table. As said, we'll use it for the two following examples.

If you already have an existing GraphQL schema that works too. Just make sure to change the GraphQL queries and mutations accordingly.

App Setup

Install React Query. For this tutorial, we're using React Query version 3.

npm install @tanstack/react-query
# or yarn
yarn add @tanstack/react-query
# or pnpm
pnpm add @tanstack/react-query

React Query Client

In src/utils/react-query-client.ts we create a QueryClient that will be used by React Query.

import { QueryClient } from '@tanstack/react-query';

export const queryClient = new QueryClient();

App

React Query requieres a QueryClient and a QueryClientProvider. This is how to set it up in e.g. App.tsx:

import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

import { queryClient } from './utils/react-query-client';
import { NewCustomer } from './components/new-customer';
import { Customers } from './components/customers';

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <div>
        <div>
          <NewCustomer />
        </div>
        <div>
          <Customers />
        </div>
      </div>
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

export default App;

React Query Fetcher

Remembered the configuration line in graphql.config.yaml above?

func: '../graphql-fetcher#fetchData'

This line tells the GraphQL Code Generator to use the fetchData function in the graphql-fetcher module for React Query. We define src/utils/graphql-fetcher.ts like this:

export const fetchData = <TData, TVariables>(
  query: string,
  variables?: TVariables,
  options?: RequestInit['headers'],
): (() => Promise<TData>) => {
  return async () => {
    const res = await fetch('<your-graphql-api>', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        ...(options ?? {}),
      },
      body: JSON.stringify({
        query,
        variables,
      }),
    });

    const json = await res.json();

    if (json.errors) {
      const { message } = json.errors[0] || 'Error..';
      throw new Error(message);
    }

    return json.data;
  };
};

The fetcher above is generic. If you're using Nhost you can look at the example here.

Get Customers (Query)

As we mentioned under the Workflow section above, we use the three-step process when using GraphQL Code Generator.

Just to repeat, these are the three steps:

  1. Manage GraphQL queries and mutations in .graphql files

  2. Run npm run codegen to generate hooks and types for queries, mutations, and subscriptions.

  3. Use the hooks and types in your React app.

Let's get going.

Add the following GraphQL query in src/graphql/customers.graphql:

query GetCustomers {
  customers {
    id
    name
  }
}

Bonus: If you're using VS Code and the GraphQL extension you'll get autocompletion when writing GraphQL in VS Code. This works because you added the graphql.config.yaml before and specified your schema and admin secret.

GraphQL Autocomplete in VS Code

GraphQL Autocomplete in VS Code

Run the codegen to generate types and hooks:

npm run codegen

Use the generated hook in React:

import { useGetCustomersQuery } from '../utils/__generated__/graphql';

export function Customers() {
  const { data, isLoading, isError, error } = useGetCustomersQuery();

  if (isLoading || !data) {
    return <div>Loading</div>;
  }

  if (isError) {
    console.error(error);
    return <div>Error</div>;
  }

  const { customers } = data;

  return (
    <div>
      <h2>Customers</h2>
      <ul>
        {customers.map((customer) => (
          <li key={customer.id}>{customer.name}</li>
        ))}
      </ul>
    </div>
  );
}

That's it. You now have type-safety from Postgres, to GraphQL to TypeScript. Good job!

Now, let's see some other benefits.

If you're trying to access a property on customers that does not exist, TypeScript will throw an error. E.g. if you're trying to access customer.address.

ERROR in src/components/customers.tsx:21:43
TS2339: Property 'address' does not exist on type '{ __typename?: "customers" | undefined; id: number; name: string; }'.
    19 |       <ul>
    20 |         {customers.map((customer) => (
  > 21 |           <li key={customer.id}>{customer.address}</li>
       |                                           ^^^^^^^
    22 |         ))}
    23 |       </ul>
    24 |     </div>

If you're using VS Code you also see the error from TypeScript while you're coding

VS Code shows TypeScript error.

VS Code shows TypeScript error.

TypeScript also knows that customer.name is of type string, which it has inferred from Postgres (customers.name is of type text) and GraphQL (customers.name is of type string). This means you can't use customer.name to perform arithmetic operations.

VS Code shows another TypeScript error.

VS Code shows another TypeScript error.

As you see, with the GraphQL Code Generator (and Postgres, GraphQL, and TypeScript) you're less likely to run into type errors. Overall, your application becomes more robust with fewer bugs!

Let's continue and see how to use GraphQL Code Generator with GraphQL mutations.

Insert Customer (Mutation)

Add the following GraphQL mutation in the same src/graphql/customers.graphql file:

mutation InsertCustomer($customer: customers_insert_input!) {
  insert_customers_one(object: $customer) {
    id
  }
}

Again, run the codegen script to generate new types and hooks for our newly added GraphQL mutation:

npm run codegen

Now, use the generated useInsertCustomerMutation hook in your React app.

import { useState } from 'react';
import { queryClient } from '../utils/react-query-client';
import { useInsertCustomerMutation } from '../utils/__generated__/graphql';

export function NewCustomer() {
  const [name, setName] = useState('');

  const { mutate, isLoading, isError, error } = useInsertCustomerMutation({
    onSuccess: () => {
      queryClient.invalidateQueries('GetCustomers');
    },
  });

  const handleSubmit = async (e: React.SyntheticEvent<HTMLFormElement>) => {
    e.preventDefault();

    try {
      await mutate({
        customer: {
          name,
        },
      });
    } catch (error) {
      return console.error(error);
    }

    setName('');

    alert('Customer added!');
  };

  return (
    <div>
      <h2>New Customer</h2>
      <div>
        <form onSubmit={handleSubmit}>
          <div>
            <input
              type="text"
              name="name"
              placeholder="Name"
              value={name}
              onChange={(e) => setName(e.target.value)}
            />
          </div>
          {isError && <div>Error: {JSON.stringify(error, null, 2)}</div>}
          <div>
            <button type="submit" disabled={isLoading}>
              Add
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

GitHub Repository

The example code for this blog post is on GitHub - GraphQL Code Generator Example Repository.

Summary

That's it. You've now a basic understanding of how to set up and use GraphQL Code Generator with React Query. This will help you build robust applications with fewer bugs. Good luck!

What's next?

Did you enjoy this blog post?

⭐ Show your support and star us on GitHub!

We use cookies to provide our services and for analytics and marketing. By continuing to browse our website, you agree to our use of cookies.
To find out more about our use of cookies, please see our Privacy Policy and Cookies Policy.