Combining the power of React Query and GraphQL for data fetching and state management
Tags: javascript, react, typescript, webdev
Published: 2021-11-02 19:53:32

On the [first part]((https://dev.to/juandj/using-react-query-to-solve-state-management-in-your-react-app-4kf9)) of this series, we took a look at how, at my present company, we had the chance of starting a greenfield project, and could choose the libraries that we considered best for the jobs. We chose React Query to handle state management in our React app, and, because our backend team was delayed providing the API that was needed for the projects, we used a fake API to get the library working, and demo its functionality.

All was going according to plan until we had a meeting with the backend team, and Connor, one of the engineers, set us up for a surprise: "We have been thinking and discussing this for a while, and we consider that the best solution we can offer you is to build a GraphQL server you can use to query and mutate the data".

GraphQL? That was a first. Our company has many backend applications, some offering REST API services and other are message queues working with RabbitMQ, but GraphQL was definitely not under anyone's radar.

But as I started thinking about it, it slowly started having more and more sense. The frontend application we were building would need to display lots of data in many different shapes, with some pages showing tables with rows containing only a certain selection of properties of the data, other pages showing data summaries, and in most cases we would need to build advanced filtering functionality. GraphQL's ability to serve the exact data requested by the user would save us a huge effort of re-formatting it in the frontend, prevent us from over or under-fetching, and basically tailoring every request we made exactly to our needs.

It sounded good in theory... but we had already set up our up to use React Query as our data fetching library (and state management solution!), making requests to a REST endpoint. Would be need to throw everything away and start from scratch with something like Apollo?

It took only a small revisit to the React Query docs to realize that this wasn't the case. As we said on the first part of this series, React Query's fetching mechanisms are agnostically built on **Promises**, so it can be used with literally any asynchronous data fetching client, such as Axios, the native **fetch** and even GraphQL!

The library's docs recommended a mysterious tool for leveraging the combined power of React Query and GraphQL: [GraphQL-Codegen](https://www.graphql-code-generator.com/). What was that? I had no idea at the moment, but it promised type safety, and code generation for "ready-to-use React Hooks, based on your GraphQL operations".

Digging a little deeper into the code generator's docs, we started to understand: "When we develop a GraphQL backend, there would be many instances where we would find ourselves writing the same things which are already described by the GraphQL schema [...] By analyzing the schema and parsing it, GraphQL Code Generator can output code at a wide variety of formats".

The best way to understand that is to take a look at an example of what GraphQL-Codegen does: it takes (reads!) our schema and produces -in our case- TypeScript types that we can use all across our applications, that we otherwise would have needed to write from scratch.

So, as the example in the docs show, provided we have the following GraphQL schema ir our app:

```graphql
schema {
  query: Query
}

type Query {
  user(id: ID!): User!
}

type Mutation {
  updateUser(id: ID!, input: UpdateUserInput!): User
}

type User {
  id: ID
  name: String
  username: String
  email: String
}

input UpdateUserInput {
  name: String
  username: String
  email: String
}
```

Then GraphQL-Codegen will produce the following TypeScript types:

```tsx
export type Maybe<T> = T | null;

/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
  ID: string,
  String: string,
  Boolean: boolean,
  Int: number,
  Float: number,
};

export type Author = {
  __typename?: 'Author',
  id: Scalars['Int'],
  firstName: Scalars['String'],
  lastName: Scalars['String'],
  posts?: Maybe<Array<Maybe<Post>>>,
};

export type AuthorPostsArgs = {
  findTitle?: Maybe<Scalars['String']>
};

export type Post = {
  __typename?: 'Post',
  id: Scalars['Int'],
  title: Scalars['String'],
  author: Author,
};

export type Query = {
  __typename?: 'Query',
  posts?: Maybe<Array<Maybe<Post>>>,
};
```

OK! So far so good! But what exactly does this have to do with React Query?

To understand the real power of React Query + GraphQL + GraphQL-Codegen we need get our hands dirty.

## Replacing our fake REST API with a fake GraphQL API

While the discussions continued with our backend of how their application would be structured, we decided to modify the proof of concept that we had already built (and saw in part 1 of this series) and re-write it to query and mutate data with GraphQL. 

However, for that we had used the fake API service JSONPlaceholder. That would not help us anymore, as it provides a REST interface for fetching and updating mock resources. We needed a GraphQL API!

Enter GraphQLZero to the rescue: an online GraphQL API both powered by JSONPlaceholder and serving its same data, as well as providing the schemas! Exactly what we needed.

So taking as a starting point the demo we had built as seen in Part 1, we started by adding the GraphQL schema that we would feed the Code Generator, a simplified version of [the schema provided by GraphQLZero.](https://graphqlzero.almansi.me/api) We thus created the `schema.graphql` file on inside a new `/graphql` directory:

```graphql
# Example schema taken from https://graphqlzero.almansi.me/api and simplified
type Query {
  user(id: ID!): User!
}

type Mutation {
  updateUser(id: ID!, input: UpdateUserInput!): User
  deleteUser(id: ID!): Boolean
}

type User {
  id: ID
  name: String
  username: String
  email: String
}

input UpdateUserInput {
  name: String
  username: String
  email: String
}

input AddressInput {
  street: String
  suite: String
  city: String
  zipcode: String
}
```

You can take a look at [the detailed docs on what GraphQL schemas are and how to write them](https://graphql.org/learn/schema/), but as you can see from our file, we defined the schema for a `User` with a set of properties, as well as the `Query` to retrieve one or many of them, and `Mutations` to update and delete them.

## Creating our GraphQL documents

The next step was to define our GraphQL documents. There are actually four types: `[query`s, `mutation`s, `fragment`s and `subscription`](https://graphql.org/learn/queries/)s, but for our use case we needed only queries -to fetch the data- and mutations -to update data-, as we had declared in our `schema.graphql`.

For each query and mutation that we want to perform in our application, we need to define an individual document that our GraphQL-Codegen can later understand and transform into usable TypeScript/React code.

Our simplest case is the **query** for retrieving an individual user: it retrieves the `id` and `name` of a `User` when the `id` is passed as a parameter. We therefore created our `user.graphql` document file and placed it in the new `/graphql/queries` path:

```graphql
query getUser($id: ID!) {
  user(id: $id) {
    id
    name
  }
}
```

We needed also an additional **query** that retrieves multiple users, a `UsersPage` object type, with two sub-properties: firstly, a `data` object which consists of an array of `Users`, each of which will return the `id` and `name` properties; secondly, a `meta` object, which provides a `totalCount` property (total number of `Users` returned). We named this file `users.graphql` :

```graphql
query getUsers($options: PageQueryOptions) {
  users(options: $options) {
    data {
      id
      name
    }
    meta {
      totalCount
    }
  }
}
```

### **Example of `mutation` document**

What about updating `User` ? In order to do that, we need to describe a `mutation` that updates a `User`'s properties, by passing as parameters the `ID` of the user to update, as well as the properties to update in the shape of `UpdateUserInput` input type.

To keep our `/graphql` directory organized, we created a further subdirectory called `/mutations` and saved our `updateUser.graphql` file there:

```graphql
mutation updateUser($id: ID!, $input: UpdateUserInput!) {
  updateUser(id: $id, input: $input) {
    id
    name
  }
}
```

Everything seemed to be taking shape. We had now a new `/graphql` folder with the following tree:

```bash
├── graphql
│   ├── mutations
│   │   └── updateUser.graphql
│   ├── queries
│   │   ├── user.graphql
│   │   └── users.graphql
│   └── schema.graphql
```

## Using GraphQL-Codegen to generate the code for us

So far so good. But all we have up until now are just a number of GraphQL documents with not much utility per se. How do we actually use them to retrieve and modify our data?

This is where GraphQL-Codegen comes into place: a tool that works as the "glue" between React-Query and GraphQL. We will use it to not only automatically generate TypeScript types based on the schema we described above, but also -and this is where it really shines- to generate ready-to-use React Hooks based on each of the documents we just wrote! 

So, no more writing hooks to fetch or modify data by hand, just define a GraphQL document, run the code generator, and you'll have a hook at your disposal that leverages all the power of React-Query.

Let's get started with **GraphQL-Codegen.** Small note before though: the tool works for a wide array of languages and libraries, not only TypeScript and GraphQL. This is just one of the things it can do, and we are using this combination because this is how our app is written and what our backend looks like. But [take a look at the docs](https://www.graphql-code-generator.com/docs/getting-started/index) to see all the possibilities it offers!

### Getting started with GraphQL-Codegen

To get started, we first need to install `graphql` as well as three dev dependencies from `@grapql-codegen` : the `cli` for running our commands; `typescript-operations` , a plugin that generates the TS types out of our GraphQL schema and operations, and finally `typescript-react-query` , which generates the React Query with TS typings for us:

```bash
yarn add graphql
yarn add --dev @graphql-codegen/cli @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-query
```

As a following step, let's create a script  in our `package.json` file, that we can run to get our code automatically generated using our newly installed CLI:

```json
"scripts": {
	"generate": "graphql-codegen"
}
```

And now we move forward to the most important step: configuring the `codegen.yml` file. This is the configuration file where we indicate GraphQL-Codgen what file it should create, where to generate it and point to which schemas and operations it should take into account. There is also a number of additional configuration options, some of which fit our use case.

Let's take a look at the finished file and then we can dive deeper into what it all means:

```yaml
schema: "./graphql/schema.graphql"
documents: 
  - "./graphql/queries/**.graphql"
  - "./graphql/mutations/**.graphql"
generates:
  ./src/_generated.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      defaultScalarType: unknown
      skipTypename: true
      fetcher:
        endpoint: "https://graphqlzero.almansi.me/api"
        fetchParams:
          headers:
            content-type: "application/json"
```

- `schema`: a path string to a local GraphQL schema file or a URL to a GraphQL schema provided externally. It should provide schemas for our data types as well as operations (Query and Mutation). This option also supports multiple schemas, that can be provided as an array of strings, and they will be merged. In our case, we point to our single `schema.graphql` file within our `graphql` directory.
- `documents`: a path string that points to our GraphQL documents: query, mutation, subscription and fragment. Wildcards can be used to select all `.graphql` files under a directory: for our case, we will use an array to point to all `*.graphql` documents within our `/graphql/queries` and `/graphql/mutations` directories.
- `generates`: a key-value map where the key represents an output path for the generated code and the value represents a set of options which are relevant for that specific file. We will generate our code directly within our `/src` folder.
    - `generates.plugins`: a required list of plugins that the code generator needs to auto-generate types and hooks based on our schema and documents. For our React-Query use case we need the plugins which we have previously installed:
        - `typescript`
        - `typescript-operations`
        - `typescript-react-query`
    - `generates.config`: a map used to pass additional configuration to the plugins. We are currently using:
        - `generates.config.defaultScalarType`: instructs the plugin to override the type that unknown scalars will have. Default value is `any`, but our config overrides it to `unknown` due to avoid having `any` types in our codebase.
        - `generates.config.skipTypename`: instructs the plugin not to add the `__typename` property to the generated types. Since we do not initially need to differentiate our objects types through their type, the default value is overriden to `false`.
        - `generates.config.fetcher`: customizes the `fetcher` function we wish to use in the generated file, and that will be responsible of making requests to our backend:
            - `generates.config.fetcher.endpoint`: since we will point to a unique endpoint exposed by our GraphQL server, we can configure it in this property. This prevents us from having to pass in the endpoint every time we use one of the generated React Hooks.
            - `generates.config.fetcher.fetchParams`: allows to set additional parameters to our `fetcher` function such as headers. We'll set the `content-type` header to `application/json`.

Notice that you can also configure `codgen.yml` to create multiple generated files with their own distinct schema, operations or config [by structuring the file in an alternative way.](https://www.graphql-code-generator.com/docs/getting-started/schema-field#multiple-schemas-and-client-side-schema)

Let's go ahead and run our code generator by running:

```bash
yarn generate
```

If we take a look at the `_generated.ts` file created within `/src` we can first see how our `fetcher` function was automatically generated, already pointed at our pre-defined endpoint:

```tsx
function fetcher<TData, TVariables>(query: string, variables?: TVariables) {
  return async (): Promise<TData> => {
    const res = await fetch("https://graphqlzero.almansi.me/api", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ query, variables }),
    });

    const json = await res.json();

    if (json.errors) {
      const { message } = json.errors[0];

      throw new Error(message);
    }

    return json.data;
  }
}
```

It's also interesting to see how the generator creates TypeScript types based on our schema. For example:

```tsx
export type Maybe<T> = T | null;

export type Scalars = {
  ID: string;
  String: string;
  Boolean: boolean;
  Int: number;
  Float: number;
};

export type Query = {
  user: User;
};

export type User = {
  email?: Maybe<Scalars['String']>;
  id?: Maybe<Scalars['ID']>;
  name?: Maybe<Scalars['String']>;
  username?: Maybe<Scalars['String']>;
};
```

We'll later use these types along our codebase. But more interestingly, let's see how our tool generated ready-to-use React hooks (based on the React-Query hooks!) that completely handle data fetching and updating.

For example, let's take a look at the `useGetUserQuery` hook, that we can use to fetch a single user by passing an `ID` to it:

```tsx
import { useQuery, UseQueryOptions } from 'react-query';

export type GetUserQuery = {
  user: {
    id?: string | null | undefined,
    name?: string | null | undefined 
  }
};

export type GetUserQueryVariables = Exact<{
  id: Scalars['ID'];
}>;

export const GetUserDocument = `
    query getUser($id: ID!) {
      user(id: $id) {
        id
        name
      }
    }
`;

export const useGetUserQuery = <
  TData = GetUserQuery,
  TError = unknown
>(
  variables: GetUserQueryVariables,
  options?: UseQueryOptions<GetUserQuery, TError, TData>
) =>
    useQuery<GetUserQuery, TError, TData>(
      ['getUser', variables],
      fetcher<GetUserQuery, GetUserQueryVariables>(GetUserDocument, variables),
      options
    );
```

Notice how the generator first creates the types it needs based on the schema we provided, as well as on the query document. It then uses those types to create a hook that reutilizes React Query's `useQuery` and passes down the types as generics, the query parameters as variables, and the `fetcher` function we saw above, which is responsible for actually making the request.

### Using our hooks to fetch data

We are now ready to leverage the combined power of React Query and GraphQL. For demonstration purposes, let's create a component that takes an `id` as input from the user of our app, calls the `useGetUserQuery` to fetch a User from our GraphQLZero API and display it on screen.

```tsx
import React, { useState, ChangeEvent } from "react";
import { useGetUserQuery } from "./_generated";

export const UserDisplay = () => {

  const [userId, setUserId] = useState("1")
  const updateUserId = (event: ChangeEvent<HTMLInputElement>) => {
    setUserId(event.target.value);
  }

  const {
    isLoading,
    data,
    isError
  } = useGetUserQuery({id: userId})

  if (isError || !data) {
    return <span>Error. Please reload page.</span>;
  }

  const { user } = data;

  return (
    <section>
      <h3>Select a User ID between 1 and 10: </h3>
      <input type="number" min={1} max={10} value={userId} onChange={updateUserId}/>
      {isLoading ? 
        <p>Loading...</p>
      : (
        <div className="userRow">
          <h3>{user?.name}</h3>
          <p>User Id: {user?.id}</p>
        </div>
      )}
    </section>
  );
};
```

Notice how we use `useGetUserQuery` in a way that is analogous to the use of the common `useQuery` hook provided by the React Query library. In this case, we just pass the `userId` state as the `id` so that every time that it updates, the hook is re-run, and a request is made to our GraphQL backend with it as a parameter! Pretty amazing stuff.

![graphqlreact.gif](http://g.recordit.co/RVCEE1xDIv.gif)

### Wrapping it up

We have now seen how we can leverage the combined power of React Query and GraphQL to easily and flexibly handle data fetching and updating. By simply defining our GraphQL schemas and documents and taking advantage of the fantastic GraphQL-Codgen tool, handling our data needs becomes a breeze that really accelerates the development experience, and pushes our codebases to be more maintainable with reusable types and React hooks.

If you have an app that consumes a GraphQL endpoint, be sure to give these tools a try.

Check out [the finished demo app](https://react-query-graphql-demo.vercel.app/) and [clone the repo](https://github.com/jpdjere/react-query-graphql-demo) to play around with the code.

Thanks for reading!