GraphQL Code Generator with TypeScript, React and Apollo Client
Tags: graphql, typescript, react, apollo
Published: 2023-12-29 09:55:23

GraphQL Code Generator is a powerful, open-source, plugin-based tool for streamlining your development by generating boilerplate code from a GQL schema. It can automate the generation of 
 - Typed Queries, Mutations and, Subscriptions for React, Vue, Angular, Next.js & Svelte, whether you are using Apollo Client, URQL or, React Query.
 - Typed GraphQL resolvers, for any Node.js (GraphQL Yoga, GraphQL Modules, TypeGraphQL or Apollo) or Java GraphQL server.
 - … And more!

This was my first experience with GraphQL (GQL) and the codegen tool so I'm going to start with what my situation is and how I went about installing and configuring it to serve my use case. I'll include links to additional info as I go.

## My Setup
The web apps I work on are written in React and TypeScript. We recently migrated some of our servers to use GraphQL instead of REST, so naturally I turned to Apollo Client and started writing react hooks that extended the native `useQuery` or `useMutation` hooks in Apollo.
{% collapsible Example %}
```typescript
import { gql, useQuery } from '@apollo/client';

const CHARACTERS_QUERY = gql`
  query GetCharacters {
    characters {
      results {
        name
        origin {
          name
        }
      }
    }
  }
`;

export default function useCharacters() {
  return useQuery(CHARACTERS_QUERY);
}
```
{% endcollapsible %}

This approach became a problem though because obviously the queries were not type safe and so I would need to write out all the TypeScript types, its also a pain to extend! As the application/s grow, I kept manually writing these query/mutation hooks, which became time consuming. There is however a way to use [TypeScript with Apollo Client](https://www.apollographql.com/docs/react/development-testing/static-typing/) which in turn lead me to the [GraphQL Code Generator](https://the-guild.dev/graphql/codegen).

### Generating Types
*[Source Repo](https://github.com/CodeNameGrant/gql-codegen/tree/generate-types)*
The first step I had to take was to generate the TypeScript types from the GQL schema. 

**Install the generator's CLI as well as the TypeScript plugin**
```bash
npm i -D @graphql-codegen/cli @graphql-codegen/typescript
```

**Add a script to your `package.json` execute the generator**
```json
scripts: {
  ...
  "codegen": "graphql-codegen"
}
```

**Create the config file in your root directory named `codegen.ts`**
```typescript
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  overwrite: true,
  schema: "https://rickandmortyapi.com/graphql",
  generates: {
    "src/graphql/generated/graphql.ts": {
      plugins: ['typescript'],
    }
  }
};

export default config;
```
This config will inform the generator to use the Rick & Morty API to generate a file (`graphql.ts`) using the [TypeScript plugin](https://the-guild.dev/graphql/codegen/plugins/typescript/typescript). This will result in the GQL schema being converted into basic TypeScript Types.

### Generate React/Apollo Hooks
*[Source Repo](https://github.com/CodeNameGrant/gql-codegen/tree/generate-hooks)*
The next step will instruct the generator to use predefined queries (or mutations) to generate hooks that execute the queries using Apollo Client.

**Install the [TypeScript-Operations](https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-operations) and [TypeScript React Apollo](https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-react-apollo) plugins.**
```bash
npm i -D @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo
```

**Create `src/graphql/documents.graphql` to store the queries to generate hooks for.**
```graphql
query Episodes {
  episodes {
    results {
      episode
      name
    }
  }
}

query Characters {
  characters {
    results {
      name
      species
    }
  }
}
```

**Update the `codegen.ts` file to use the above file and plugins to generate the result**
```typescript
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  overwrite: true,
  schema: "https://rickandmortyapi.com/graphql",
  documents: './**/*.graphql',
  generates: {
    "src/graphql/generated/graphql.ts": {
      plugins: ['typescript', 'typescript-operations', 'typescript-react-apollo'],
    },
    config: {
      withHooks: true
    }
  }
};

export default config;
```
The GQL [`documents`](https://the-guild.dev/graphql/codegen/docs/config-reference/documents-field) can take different patterns. However, after some experimentation, I found that for my use case its easier to just keep them all together in a `.graphql` file. The `config` attribute is where plugin attributes are specified. The `withHooks` attribute is used by `typescript-react-apollo`. 

**`npm run codegen`**
Once you have run the generator again, you will see that the generated `graphql.ts` file now contains hooks that execute the various query variants from Apollo based on the queries we declared, ie `useEpisodesQuery`, `useEpisodesLazyQuery`, `useEpisodesSuspenseQuery`, etc.

### Generate Hooks near operational files
*[Source Repo](https://github.com/CodeNameGrant/gql-codegen/)*

As I'm sure you noticed from the source code, the app is structured with feature directories but our types and hooks are all generated in single file. However, I want the types to be in their own file to be used anywhere in the app but the documents and generated hooks must be in the feature directories where they are being used.

To achieve this, I used the [Near Operation File Preset](https://the-guild.dev/graphql/codegen/plugins/presets/near-operation-file-preset) plugin. This plugin scans a directory for documents and generates the hooks files in the same location.

**First install the plugin**
```bash 
npm i -D @graphql-codegen/near-operation-file-preset
```

**In order to use this preset, you need to add 2 outputs to your `codegen.ts` file:**
 - The first, is the base types, generated by typescript plugin.
 - The second, is the one that is in charge of generating types per document.
```typescript
import { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  overwrite: true,
  schema: '',

  generates: {
    './src/types.ts': {
      plugins: ['typescript']
    },
    './src/': {
      documents: './**/*.graphql',
      preset: 'near-operation-file',
      presetConfig: {
        extension: '.generated.ts',
        baseTypesPath: 'types.ts'
      },
      plugins: ['typescript-operations', 'typescript-react-apollo'],
      config: {
        withHooks: true
      },
    }
  }
};

export default config;
```
**`npm run codegen`**
This will result in a `*.generated.ts` file being created for each `.graphql` in any directory under `src`.

## Final Thoughts
The GraphQL Code Generator is in fact a powerful tool that has simplified my development process. It has a large [plugin library](https://the-guild.dev/graphql/codegen/plugins) that supports many languages and presets and is generally well documented.