What's new in GraphQL-Codegen v2?
Tags: graphql, codegen, typescript
Published: 2021-08-05 11:44:01

> This article was published on Tuesday, August 3, 2021 by [Dotan Simha](https://twitter.com/dotansimha) @ [The Guild Blog](https://the-guild.dev/)

[GraphQL Code Generator](https://github.com/dotansimha/graphql-code-generator) is around for almost
5 years, and it's improving the developer experience of many developers (>3M downloads a month on
NPM!). Like all The Guild's projects, throughout all these years, the library continuously evolved
based on the experience and feedback from the community and our clients - every day codegen gets
better and better, so keep your feedback coming and keep your dependencies up to date :) But now,
it's time for a big release!

We recently decided to revisit and improve some parts of codegen, and we're happy to announce that
we're releasing a new version of the tool (`v2`)!

This release is all about improving the reliability and readability of the generated code, and
adding new features (and a new plugin/preset that might revolutionize how we generate code on our
frontends!).

## Why `v2`?

Until now GraphQL Codegen has evolved, without the need for (major) breaking changes, but there were
a couple of things we wanted to do for a better and easier codegen experience that required some
small breaking changes.

We are currently working on adding ESM support for all [The Guild](/)'s tools.

Unfortunately, in Codegen, we are not there yet
([you can track the progress here](https://github.com/dotansimha/graphql-code-generator/pull/6102),
because we need to make sure that plugins, presets, schemas and documents are still being loaded
correctly. But it's coming soon!

But other tools, which we use as a dependency, like
[`graphql-tools` (v8) already supports ESM](/blog/graphql-tools-v8), and dropped support for Node
10, so we are aligning to that.

We've updated to the latest version of [`graphql-tools`](https://graphql-tools.com) (v8) and
[`graphql-config`](https://graphql-config.com) (v4) to get some upstream bug fixes.

## Human-Readable Generated Types

A few years ago, we changed the output of codegen to use `Pick` in order to build the operation
types based on the schema types:

```ts
// Base types generated based on the schema, from `typescript` plugin:
type Query = {
  user: User
}

type User = {
  id: Scalars['ID']
  name: Scalars['String']
}

// Types generate based on operations, from `typescript-plugins`:

type MyQuery = {
  user: Pick<User, 'id' | 'name'>
}
```

Some time after, we added `preResolveTypes` configuration flag as experimental, in order to generate
more readable types with primitive types resolved directly on the generate types:

```ts
type MyQuery = {
  user: {
    id: string
    name: string
  }
}
```

We are happy to announce that v2 of `typescript-operations` now uses `preResolveTypes: true` by
default, so all generated types are more readable.

> As a user of Codegen this is not a breaking change (aside that the generated code is slightly
> different), and all your types are still fully compatible with `v1` of the tool. You can set
> `preResolveTypes: false` if you prefer to keep the old behavior.

## Type Accuracy

In addition to more readable types, we also improved the accuracy of the generated types.

A few months ago, [`chrbala`](https://github.com/chrbala) found
[a bug in the `typescript-operations` plugin](https://github.com/dotansimha/graphql-code-generator/issues/6149),
which was causing the generated types to be incorrect in cases where you use nested and complex
fragments.

This bug was caused by the fact that we were combining the fragment types using the `&` operator of
TypeScript, and this operator doesn't apply deep merging for nested sub-types. So when using
multiple fragments (`MyFirstFragment & MySecondArgument`), the nested fields are being overwritten,
instead of being merged.

[`n1ru4l`](https://github.com/n1ru4l) picked that up and
[fixed the bug](https://github.com/dotansimha/graphql-code-generator/pull/6184) and added support
for better handling of these kind of cases.

The new configuration flag (`inlineFragmentTypes: inline`) is now used by default, and generates
more accurate types, without introducing breaking changes!

Instead of combining the fragment types they are merged and inlined.

```diff
export type FeedQuery = { __typename?: 'Query' } & {
-  currentUser?: Maybe<{ __typename?: 'User' } & Pick<User, 'login'>>;
-  feed?: Maybe<Array<Maybe<{ __typename?: 'Entry' } & FeedEntryFragment>>>;
+  currentUser?: Maybe<{ __typename?: 'User'; login: string }>;
+  feed?: Maybe<
+    Array<
+      Maybe<{
+        __typename?: 'Entry';
+        id: number;
+        commentCount: number;
+        score: number;
+        createdAt: number;
+        repository: {
+          __typename?: 'Repository';
+          full_name: string;
+          html_url: string;
+          description?: Maybe<string>;
+          stargazers_count: number;
+          open_issues_count?: Maybe<number>;
+          owner?: Maybe<{ __typename?: 'User'; avatar_url: string }>;
+        };
+        vote: { __typename?: 'Vote'; vote_value: number };
+        postedBy: { __typename?: 'User'; html_url: string; login: string };
+      }>
+    >
+  >;
};
```

No worries, fragment types are still exported separately!

> All your types are still fully compatible with `v1` of the tool. You can set
> `inlineFragmentTypes: combine` if you still prefer to keep the old behavior.

## Removal of Deprecations

We also used the need for a major version in order to remove a few deprecations from the codebase.

#### `typescript-resolvers`

In `typescript-resolvers`, we had a generated signature for `IResolvers` and `IDirectiveResolvers`
(which were deprecated 2 years ago), `v2` of this plugin is removing these proxy types.

Also, the `noSchemaStitching` flag is now set to `true` by default, so the generated resolvers
signature is simpler and matches the needs of most projects.

> If you are using Schema Stitching, you can set `noSchemaStitching: false` to keep the old
> behavior.

#### `typescript-compatibility`

The `typescript-compatibility` plugin was created to make it easier to migrate from v0 to v1 of
codegen, and hasn't been actively developed for a few years.

With this release, we no longer support or maintain the `typescript-compatibility` plugin. If you
are still using it, please consider migrating to the new type format.

## New TypeScript Plugin for Operations!

Some time ago we started advocating the
[`near-operation-file`](https://graphql-code-generator.com/docs/presets/near-operation-file) preset,
for generating GraphQL client code next to the `.graphql` operation file.

A month ago, [`Maël Nison`](https://github.com/arcanis) reached out to us with
[a new concept](https://github.com/arcanis/graphql-typescript-integration) for matching your actual
GraphQL operation string and the generated TypeScript types. Without having to add clumsy import
statements for each GraphQL operation.

It allowed writing the operations in the following way:

```ts
import { gql } from './gql'

const { GetTweets, CreateTweet } = gql(`#graphql
  query GetTweets {
    Tweets {
      id
    }
  }

  mutation CreateTweet {
    CreateTweet(body: "Hello") {
      id
    }
  }
`)
```

The README on his repository contained a
[list of limitations](https://github.com/arcanis/graphql-typescript-integration#limitations). We
took that that feedback and fixed some upstream bugs within graphql-tools and improved
graphql-codegen according to that.

With a few adjustments, [`n1ru4l`](https://github.com/n1ru4l) managed to
[turn that amazing idea into a plugin and a preset](https://github.com/dotansimha/graphql-code-generator/pull/6267)
that extends the behavior of `typescript-operation` and `TypedDocumentNode`, and allow you to
generate TypeScript types that magically matches your GraphQL query strings (this also required a
few fixes in `graphql-tools`, so now the Loaders in graphql-tools are better!).

#### So What Does That Mean?

Today, GraphQL Codegen scans your codebase and looks for all the operations that are being used by
your components (or, from `.graphql` files), then it generates types and wraps `TypedDocumentNode`
for you. Those are either generated to a single file, from which you import from anywhere within the
app or next to the `.graphql` when using the `near-operation-file-preset`. **Folder structure
(`near-operation-file-preset`)**

```
Project
- src
  - UserQuery.graphql
  - UserQuer.graphql.tsx
```

With this new plugin+preset, you can generate typings for your inline `gql` function usages, without
having to manually specify import statements for the documents. All you need to do is import your
`gql` function and run codegen in watch mode.

```tsx
import { FragmentType, gql } from '@app/gql'

// TweetFragment is a fully typed document node
const TweetFragment = gql(/* GraphQL */ `
  fragment TweetFragment on Tweet {
    id
    body
  }
`)

const TweetsQueryWithFragment = gql(/* GraphQL */ `
  query TweetsWithFragmentQuery {
    Tweets {
      id
      ...TweetFragment
    }
  }
`)

const TweetComponent = (props: { tweet: FragmentType<typeof TweetFragment> }) => {
  return <Tweet>{props.tweet.body}</Tweet>
}
```

That way we don't need to switch context between `.graphql` and our `.ts(x)` files and have less
files within our repository (compared to the `near-operation-file` preset)!

You can find
[the complete documentation, examples and API reference in codegen website](https://graphql-code-generator.com/docs/presets/gql-tag-operations).

[We also have an example PR for a migration from `near-operation-file` to the `gql-tag-operations-preset`.](https://github.com/n1ru4l/character-overlay/pull/331)

We want to hear your thoughts on this way of using Codegen on the frontend.

Based on your feedback we might want to make this the recommended way of using Codegen on the
frontend in the future.

> Huge thanks to [Maël Nison](https://github.com/arcanis), who conceptualized the foundation for
> this preset [over here](https://github.com/arcanis/graphql-typescript-integration). Please keep
> pushing the boundaries!

## What's Next?

Like we said at the beginning, we continuously keep improving Codegen, so this release is not the
end but just a start. Here are some sneak peeks on things we are currently working on:

### ESM Support

ESM support is coming soon (and will probably result in another major version)

### SDL Resolver Development Flow Improvements

We are experimenting with a better way to link `typeDefs` with the resolvers signature generated by
`typescript-resolvers` in a similar way to the new `gql-tag-operations` plugin. One of the goal of
this preset is to make sure that every resolver that should be defined and implemented is actually
implemented.

```ts
import { typeDefs } from 'tsgql'

const sdl = typeDefs(/* GraphQL */ `
  type Query {
    foo: String!
  }
`)

export const resolvers: typeof sdl = {
  Query: {
    // If this resolver would not be defined it will raise a TypeScript error.
    foo: () => 'test'
  }
}
```

[You can find more info here](https://github.com/dotansimha/graphql-code-generator/pull/6379)

### Fragment Type Masking

Today the Relay framework development flow allows hiding (masking) properties from operation results
objects that are added via fragments. Those properties are only accessible from within the component
that consumes the fragment:

```tsx
import { FragmentProperty, gql } from '@app/gql'

// TweetFragment is a fully typed document node
const TweetFragment = gql(/* GraphQL */ `
  fragment TweetFragment on Tweet {
    id
    body
  }
`)

const TweetsQueryWithFragment = gql(/* GraphQL */ `
  query TweetsWithFragmentQuery {
    Tweets {
      id
      ...TweetFragment
    }
  }
`)

const AppComponent = () => {
  const { data } = useQuery({ query: TweetsQueryWithFragment })

  // accessing this here is a TypeScript error.
  data.Tweets[0].body

  return (
    <>
      {data.Tweets.map(tweet => (
        <TweetComponent key={tweet.id} tweet={tweet} />
      ))}
    </>
  )
}

const TweetComponent = (props: { tweet: FragmentProperty<typeof TweetFragment> }) => {
  // accessing props.tweet.body here is legit.
  return <Tweet>{props.tweet.body}</Tweet>
}
```

This allows building scalable components as each component only receives the data it should have
access to and furthermore components higher up in the tree can not access data it did not explicitly
request. Removing a component will thus not result in surprising behaviors.

### What Else Is Missing?

Do you think something else is missing or could be improved? Reach out to us via the chat on this
page, a [GitHub discussion](https://github.com/dotansimha/graphql-code-generator/discussions) or on
the [GraphQL Discord](https://discord.gg/x3MYZRvMrg).