Integrating GraphQL Code Generator in your frontend applications
Tags: graphql, codegen, typescript, react
Published: 2021-07-29 08:58:19

> This article was published on Wednesday, May 20, 2020 by [Dotan Simha](https://twitter.com/dotansimha) @ [The Guild Blog](https://the-guild.dev/)

In this article we'll try to explain and demonstrate common patterns for frontend development with
GraphQL and GraphQL Code Generator.

Most patterns are general and can be applied to most popular frontend frameworks (React, Angular,
Vue, Stencil), and to popular GraphQL client libraries (Apollo / urql), due to extended support of
GraphQL Code Generator, and it's flexibility.

In this article, we'll cover the development workflow of frontend applications with *TypeScript* and
*GraphQL Code Generator*, suggest best-practices for GraphQL development for frontend developers,
and try to explain the idea behind it.

## Why Do I Need GraphQL Code Generator in My Project?

Let's start by understanding the need for GraphQL Code Generator in your project.

If you are using TypeScript for frontend development, you probably aim to get the most out of the
TypeScript type-system, that means, that your preference should be to have typed variables all
across your application.

It starts with the code you write - UI components, services and business logic. You can also have
type safety for your third-party libraries (some built-in, and some with `@types/...` packages).

The idea behind type-safety is to make sure that your code can be statically analyzed and built,
before running it. It's useful because this way your can detect potential bugs before they happen in
runtime.

### *But What about the Data Your Fetch from External Services?*

So if you are already using GraphQL, you probably know that your GraphQL API is typed, and built as
a GraphQL schema.

And it doesn't matter which language or platform is used to write your GraphQL API or schema - you
fetch it the same way into your frontend application - with GraphQL operations (`query` / `mutation`
/ `subscriptions`, and `fragment`) and probably over HTTP.

So if your GraphQL schema is typed already, and your GraphQL operations allow you to choose specific
fields from it (called Selection Set), why not leverage the schema and selection set and turn it
into TypeScript types?

### Basic Data Fetching with GraphQL

Let's assume that we have the following simple GraphQL schema:

```graphql
scalar Date

type Query {
  upcomingEvents: [Event!]!
}

type Event {
  id: ID!
  title: String!
  date: Date!
  location: Location!
}

type Location {
  name: String!
  address: String!
}
```

And the client-side application consumes it with the following `query`:

```graphql
query listEvents {
  upcomingEvents {
    id
    title
    date
  }
}
```

If you client-side application only needs `id`, `title` and `date` from the `Event` type - you can
expect to have those fields in your GraphQL response.

You can also use it in your component code:

```tsx
export const ListEvents = listEvents => {
  return (
    <ul className="list-events">
      {listEvents.map(event => (
        <li key={event.id}>
          {event.title} ({event.date})
        </li>
      ))}
    </ul>
  )
}
```

In the example above we have a few issues that might be bugs in the future:

1.  We don't know the type of `listEvents` - and we can't really know it without creating a type for
    it manually (but this could also break, because the API could change).
2.  We can't be sure what are the actual types of `id`, `title` and `date` fields - it's `any`.
3.  We can't count of the fields to be there because they GraphQL query can change, and it's not
    connected to our code at all.
4.  If you'll try to access `location` of the event - you'll just get `undefined` because it's not
    part of the selection set.

With GraphQL Code Generator, you can have a full type safety, based on your GraphQL schema *and*
your GraphQL operations, and that means:

1.  You can tell what is the exact structure of `listEvents`, what could be `null` and enjoy
    auto-complete in your IDE.
2.  You can tell what is the data type of all fields.
3.  If your selection set changes, it being reflected automatically and you can detect issues while
    developing or building (instead while running).
4.  Trying to access fields that are not defined in your selection set will show an error in build
    time and in your IDE.

So those are the basic types that codegen can generate for your, and you can get those by using the
`@graphql-codegen/typescript` and `@graphql-codegen/typescript-operations` plugins of GraphQL Code
Generator.

But that's not all - you can generate much more - you can get React Hooks, Angular Services and
more.

### How Do I Start?

You can start by
[trying GraphQL Code Generator plugin in the live-demo here](https://graphql-code-generator.com) and
with the
[Getting started with GraphQL Code Generator](https://graphql-code-generator.com/docs/getting-started/installation).

## Tips & Best Practices When Using GraphQL Code Generator and TypeScript

Now that you understand why and how GraphQL Code Generator can help you, it's time to learn new
concepts that might simplify the way your consume GraphQL API, and improve your code quality.

### Watch Mode

GraphQL Code Generator also comes with a built-in watch mode. You can use it from the CLI:

```sh
graphql-codegen --watch
```

Or, set it in your `codegen.yml` file:

```yaml
watch: true
schema: ...
```

This way, each time you have changes for your GraphQL schema or GraphQL operations, GraphQL Code
Generator will be executed again and update the generated files.

### Generate More than Just Types

GraphQL Code Generator can generate more than just TypeScript types. It can automate some of your
GraphQL development workflow, generate common practices for data fetching, and add type-safety to
code you usually need to write manually.

Beside TypeScript types, here's a list and examples of *part* of GraphQL Codegen capabilities:

#### Dump Remote Schema to a Local File

If your GraphQL schema is only available for you using an HTTP endpoint, you can always get a copy
of it locally. This is useful for better IDE experience.

You can do it with the `@graphql-codegen/schema-ast` plugin, and the following configuration:

```yaml
schema: http://YOUR_SERVER/graphql
generates:
  ./src/schema.graphql:
    plugins:
      - schema-ast
```

*   [`@graphql-codegen/schema-ast` docs](https://graphql-code-generator.com/docs/plugins/schema-ast)

#### Save Local GraphQL Introspection

GraphQL schema can be represented in many ways. One of the is
[introspection](https://graphql.org/learn/introspection).

You can save a local copy of your schema introspection using `@graphql-codegen/introspection` and
the following:

```yaml
schema: YOUR_SCHEMA_PATH
generates:
  ./src/schema.json:
    plugins:
      - introspection
```

*   [`@graphql-codegen/introspection` docs](https://graphql-code-generator.com/docs/plugins/introspection)

#### Add Custom Content to Output Files

I wish you to add custom content to the codegen output files, you can use `@graphql-codegen/add`
plugin, and add your content this way:

```yaml
schema: YOUR_SCHEMA_PATH
generates:
  ./src/types.ts:
    plugins:
      - add: '// THIS FILE IS GENERATED, DO NOT EDIT!'
      - typescript
```

*   [`@graphql-codegen/add` docs](https://graphql-code-generator.com/docs/plugins/add)

#### React & Apollo: Generate Hooks

You can generate ready-to-use React hooks for your GraphQL operations, with the following
configuration:

```yaml
schema: SCHEMA_PATH_HERE
documents: './src/**/*.graphql'
generates:
  src/generated-types.tsx:
    config:
      withHooks: true
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
```

And then use it in your code:

```tsx
import React from 'react'
import { useMyQuery } from './generated-types'

export const MyComponent: React.FC = () => {
  const { data, loading, error } = useMyQuery()

  // `data` is now fully typed based on your GraphQL query
  return <> ... </>
}
```

*   [`@graphql-codegen/typescript-react-apollo` docs](https://graphql-code-generator.com/docs/plugins/typescript-react-apollo)

#### React & Apollo: Generate HOC (High-Order-Component)

You can generate ready-to-use React HOC for your GraphQL operations, with the following
configuration:

```yaml
schema: SCHEMA_PATH_HERE
documents: './src/**/*.graphql'
generates:
  src/generated-types.tsx:
    config:
      withHOC: true
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
```

And then use it in your code:

```tsx
import React from 'react';
import { withMyQuery } from './generated-types';

const MyViewComponent: React.FC = ({ data, loading, error }) => {
  // `data` is now fully typed based on your GraphQL query

  return (<> ... </>);
};

export const MyComponent = withMyQuery({
  variables: { ... }
})(MyViewComponent);
```

*   [`@graphql-codegen/typescript-react-apollo` docs](https://graphql-code-generator.com/docs/plugins/typescript-react-apollo)

#### React & Apollo: Generate Components

You can generate ready-to-use React data components for your GraphQL operations, with the following
configuration:

```yaml
schema: SCHEMA_PATH_HERE
documents: './src/**/*.graphql'
generates:
  src/generated-types.tsx:
    config:
      withComponent: true
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
```

And then use it in your code:

```tsx
import React from 'react';
import { MyQueryComponent } from './generated-types';

export const MyComponent: React.FC = ({ data, loading, error }) => {

  return (
    <MyQueryComponent variables={...}>
      {
        ({ data, loading, error }) => {
          // `data` is now fully typed based on your GraphQL query

          return (<> ... </>)
        }
      }
    </MyQueryComponent>
  );
};
```

*   [`@graphql-codegen/typescript-react-apollo` docs](https://graphql-code-generator.com/docs/plugins/typescript-react-apollo)

#### Angular & Apollo: Generate Services

You can generate ready-to-use Angular `Service`s for your GraphQL operations, with the following
configuration:

```yaml
schema: SCHEMA_PATH_HERE
documents: './src/**/*.graphql'
generates:
  src/generated-types.ts:
    config:
      withHooks: true
    plugins:
      - typescript
      - typescript-operations
      - typescript-apollo-angular
```

And then use it in your code:

```ts
import { MyFeedGQL, MyFeedQuery } from './generated-types'

@Component({
  selector: 'feed',
  template: `
    <h1>Feed:</h1>
    <ul>
      <li *ngFor="let item of feed | async">{{ item.id }}</li>
    </ul>
  `
})
export class FeedComponent {
  feed: Observable<MyFeedQuery['feed']>

  constructor(feedGQL: MyFeedGQL) {
    this.feed = feedGQL.watch().valueChanges.pipe(map(result => result.data.feed))
  }
}
```

*   [`@graphql-codegen/typescript-apollo-angular` docs](https://graphql-code-generator.com/docs/plugins/typescript-apollo-angular)

#### React & urql: Generate Hooks

If you are using [`urql`](https://github.com/FormidableLabs/urql) as your GraphQL client, you can
generate ready-to-use React hooks for your GraphQL operations, with the following configuration:

```yaml
schema: SCHEMA_PATH_HERE
documents: './src/**/*.graphql'
generates:
  src/generated-types.tsx:
    config:
      withHooks: true
    plugins:
      - typescript
      - typescript-operations
      - typescript-urql
```

And then use it in your code:

```tsx
import React from 'react'
import { useMyQuery } from './generated-types'

export const MyComponent: React.FC = () => {
  const { data, loading, error } = useMyQuery()

  // `data` is now fully typed based on your GraphQL query

  return <> ... </>
}
```

*   [`@graphql-codegen/typescript-urql` docs](https://graphql-code-generator.com/docs/plugins/typescript-urql)

<Callout>This plugin can also generate HOC or data Component, based on your preference ;)</Callout>

#### Vue.js & Apollo: Generate Composition Functions

If you are using [`Vue.js`](https://vuejs.org) with `@vue/apollo-composable` your GraphQL client,
you can generate composition functions based on your GraphQL operations:

```yaml
schema: SCHEMA_PATH_HERE
documents: './src/**/*.graphql'
generates:
  src/generated-types.ts:
    config:
      withHooks: true
    plugins:
      - typescript
      - typescript-operations
      - typescript-vue-apollo
```

And then use it in your code:

```vue
<template>
  <div>
    {{ result.feed.id }}
  </div>
</template>

<script lang="ts">
import { createComponent } from '@vue/composition-api'
import { useTestQuery } from '../generated-types'

export default createComponent({
  setup() {
    const { result } = useMessagesQuery()

    return { result }
  }
})
</script>
```

*   [`@graphql-codegen/typescript-vue-apollo` docs](https://graphql-code-generator.com/docs/plugins/typescript-vue-apollo)

#### Apollo: Type-Safe `refetchQueries`

If you are using Apollo Client, and you wish to refetch a query when a mutation is done, you can do
add
[`@graphql-codegen/named-operations-object`](https://graphql-code-generator.com/docs/plugins/named-operations-object)
plugin to your setup.

It will generate a `const` object that contains a list of your GraphQL operation names, as found by
the codegen. This is useful because if you'll change the name of your operation, you'll know about
it in build time, and you'll be able to update it:

This is how to configure it:

```yaml
schema: SCHEMA_PATH_HERE
documents: './src/**/*.graphql'
generates:
  src/generated-types.ts:
    plugins:
      - typescript
      - typescript-operations
      - named-operations-object
```

And then use it in your code:

```tsx
import { client } from './apollo'; // this is your Apollo Client instance, for example
import { addTodoMutation, namedOperations } from './generated-types';

client.mutate({
  query: addTodoMutation,
  variables: { ... },
  refetchQueries: [
    // If you'll change or remove that operation, this will fail during build time!
    namedOperations.Query.listTodo,
  ]
})
```

<Callout>
  You can use it with any other wrapper of Apollo-Client, such as `apollo-angular` or
  `react-apollo`.
</Callout>

*   [`@graphql-codegen/named-operations-object` docs](https://graphql-code-generator.com/docs/plugins/named-operations-object)

#### Apollo: Auto-Generated `fragmentMatcher` / `possibleTypes`

If you are using Apollo-Client and your schema contains GraphQL `union` or `interface`, you'll need
to provide `fragmentMatcher` to your Apollo store instance.

This is needed in order to improve performance of Apollo store.
[You can read more about this here](https://apollographql.com/docs/react/data/fragments/#fragments-on-unions-and-interfaces).

You can generate it using the following configuration:

```yaml
schema: YOUR_SCHEMA_PATH
generates:
  ./src/fragment-matcher.ts:
    plugins:
      - fragment-matcher
```

And then pass it directly to your Apollo instance:

```ts
import { InMemoryCache } from '@apollo/client'
// generated by Fragment Matcher plugin
import introspectionResult from '../fragment-matcher'

const cache = new InMemoryCache({
  possibleTypes: introspectionResult.possibleTypes
})
```

*   [`@graphql-codegen/fragment-matcher` docs](https://graphql-code-generator.com/docs/plugins/fragment-matcher)

## Name Your Operations

It's highly important to name your GraphQL operations, because otherwise it will be difficult for
your GraphQL client to cache and manage it. It will also make it difficult for the codegen to create
easy-to-use types, and it will fallback to `Unnamed_Operation_`.

✅ Do:

```graphql
query myOperationNameHere {
  ...
}
```

❌ Don't:

```graphql
query {
  ...
}
```

<Callout>
  **Tip**: Duplicate Names

  Ensure you have unique names for your operations.

  Libraries like Apollo Client will have issues and unexpected behavior if you re-use the same
  operation name, and GraphQL Code Generator will throw an error in case of name duplications.
</Callout>

## Write Your Operations and Fragments in `.graphql` Files

You can manage your GraphQL operations in `.graphql` files, without worrying about loading it into
your application with Webpack loaders or anything else. Also, Most IDEs has better support for
autocomplete inside `.graphql` files.

GraphQL Code Generator plugins for frontend frameworks integrations (such as
`typescript-react-apollo` / `typescript-apollo-angular`) are automatically creates an executable
copy (`DocumentNode`) of your GraphQL operations in the generated code file, and it will
automatically include it withing your wrapper call.

It will add that to the output file with `Document` suffix, and `FragmentDoc` for fragments.

So you can maintain your operations in `.graphql` files, but import it from generated code file:

```ts
// MyQueryDocument and MyUserFragmentDoc are parsed `DocumentNode`
import { MyQueryDocument, MyUserFragmentDoc } from './generated-types'
```

<Callout>
  **Tip**: No need to handle imports

  If you have a query that uses a fragment, you can just use the fragment spread as-is, without the
  need to import it or maintain it in the same file.

  For example:

  ```graphql filename="user.query.graphql"
  query user {
    userById {
      ...UserFields # We don't need to import this, just use the name
    }
  }
  ```

  ```graphql filename="userfields.fragment.graphql"
  fragment UserFields on User {
    id
    name
  }
  ```

  And if you'll import `UserQueryDocument` from your generated file, it will have the fragment
  concatenated automatically.
</Callout>

## Fragment per Component

If you wish to have a simple way to manage your application complexity with multiple queries and
fragments, consider to have small fragments that defines the needs of your components.

Consider the following structure for example (for a list and item implementation):

```
src/
├── generated-types.tsx
├── list/
├──── todo-list.tsx
├──── todo-list.query.graphql
├── list-item/
├──── todo-item.tsx
├──── todo-item.fragment.graphql
├── todo-details/
├──── todo-details.tsx
├──── todo-details.fragment.graphql
├── user-profile/
├──── profile-page.tsx
├──── me.query.graphql
├──── authenticated-user.fragment.graphql
```

Then, your GraphQL `query` files can just build it's self based on the nested fragments it needs:

```graphql filename="todo-list.query.graphql"
query todoList {
  todos {
    ...TodoItemFields
    ...TodoDetailsFields
  }
}
```

```graphql filename="me.query.graphql"
query me {
  me {
    ...AuthenticatedUserFields
  }
}
```

And then, GraphQL Code Generator will generate a matching TypeScript type per each component, based
on the fragment or query that it needs.

So you can use the generated fragment type as the input for your components, and pass it directly
from the parent component easily, with type-safety:

```tsx filename="todo-list.tsx"
import React from 'react'
import { useTodoList } from '../generated-types'
import { TodoItem } from './todo-item'

export const TodoList: React.FC = () => {
  const { data, loading, error } = useTodoList()

  return (
    <>
      {data.todos.map(todo => (
        <TodoItem todo={todo} />
      ))}
    </>
  )
}
```

```tsx filename="todo-item.tsx"
import React from 'react'
import { TodoItemFieldsFragment } from '../generated-types'

export const TodoItem: React.FC = (todo: TodoItemFieldsFragment) => {
  return <div>{todo.title}</div>
}
```

<Callout>
  Please have some judgment before creating fragments, it should represent data structure that is
  specific per component. Don't abuse this mechanism by creating fragments with a single field. Try
  to group it in a way that matches your components needs.
</Callout>

## Access to Nested Generated Types

If you are already familiar with plugins such as `@graphql-codegen/typescript-operations` output
structure, you probably already know that it's built on operations and fragments.

It means that each GraphQL `query` and each GraphQL `fragment` that you have, will be converted into
a single TypeScript `type`.

That means, that accessing nested fields in your generated TypeScript types might looks a bit
complex at the beginning.

Consider the following query:

```graphql
query userById($userId: ID!) {
  user(id: $userId) {
    id
    profile {
      age
      name {
        first
        last
      }
    }
  }
}
```

The `@graphql-codegen/typescript-operations` plugin output for that `query` will be:

```ts
export type UserByIdQuery = { __typename?: 'Query' } & {
  user?: Maybe<
    { __typename?: 'User' } & Pick<User, 'id'> & {
        profile?: Maybe<
          { __typename?: 'Profile' } & Pick<Profile, 'age'> & {
              name: { __typename?: 'Name' } & Pick<Name, 'first' | 'last'>
            }
        >
      }
  >
}
```

Accessing the actual TypeScript type of `user.profile.name.first` might look a bit intimidating, but
there are several things you can do to simplify the access to it:

*   **Best solution: use fragments** - if you'll use fragments for the `User` fields and for `Profile`
    fields, you'll break down the types into smaller pieces (see previous tip).
*   Use TypeScript type system: `type FirstName = UserByIdQuery['user']['profile']['name']['first']`.
*   You can also do it with `Pick`:
    `type FirstName = Pick<UserByIdQuery, ['user', 'profile', 'name', 'first']>`.

<Callout>
  **Tip**: Hate `Pick` in your generated files?

  The `@graphql-codegen/typescript-operations` is the TypeScript representation of your GraphQL
  selection set. Just like selection set chooses fields from the GraphQL schema,
  `typescript-operations` picks fields from `typescript` plugin (which is the representation of your
  GraphQL schema).

  If you wish to have simpler TypeScript output, you can set `preResolveTypes: true` in your
  configuration, and it will prefer to use the primitive TypeScript type when possible.
</Callout>