Optimize your Bundle Size with SWC and GraphQL Codegen
Tags: clientpreset, codegen
Published: 2023-04-18 11:58:05

> This article was published on Tuesday, April 18, 2023 by [Jesse van der Velden](https://twitter.com/JesseVelden) @ [The Guild Blog](https://the-guild.dev/)

With the new
[`client-preset` for GraphQL Code Generator](https://the-guild.dev/blog/unleash-the-power-of-fragments-with-graphql-codegen)
it is possible to write your GraphQL queries inlined, next to your component in a type-safe way.
This can greatly improve the Developer Experience (DX).

Instead of writing your operations in a separate file, you can write them inline in your component,
and use the `graphql` function to parse the operation and get the correct types.

{/* prettier-ignore */}

```tsx
// Instead of operations in a separate .graphql file and importing generated hooks:
import { useGetCartByIdQuery } from './graphql/generated-hooks'
const { data } = useGetCartByIdQuery({ variables: { id: '1' } })

// You can write your operations directly next to your component and get the correct types:
import { graphql } from './graphql'
const GetCartByIdDocument = graphql(/** GraphQL */ `
  query GetCartById($id: ID!) {
    id
    items {
      id
      ...CartItem
    }
  }
`)
// Awesome! We have the correct types for this operation even when we specify it next to our component!
```

However, this approach can come at a cost - the bundle size of your pages can increase
significantly, especially as the number of queries and mutation grows. In this post, we will show
you what the problem is and how to optimize your bundle size with the new
[`@graphql-codegen/client-preset-swc-plugin`](https://www.npmjs.com/package/@graphql-codegen/client-preset-swc-plugin)
plugin for your applications supported by the SWC compiler such as projects using
[Next.js](https://nextjs.org) or [Vite](https://vitejs.dev/).

## The Generated Code When Using the `client-preset`

When using the `client-preset` without any additional optimization, your site's bundle can quickly
become bloated with unnecessary imports on every page/ component where you use GraphQL. This is
because by default `client-preset`'s `graphql` function that enables a great TypeScript DX, has to
have all the GraphQL operations available to it to parse the operation and have the correct types.

The generated code output for the `graphql` function is a function that looks similar to the
following code, where code-splitting and tree-shaking is not easily possible.

```ts
const documents = {
  'fragment CartItems on CartItem {\n name\n quantity \n  }\n  ': types.CartItemsFragmentDoc,
  'query GetCartById($id: ID!) {\n cart(id: $id) {\n id \n items { \n id \n ...CartItem\n }\n totalItems\n}\n  }\n':
    types.GetCartByIdDocument
}
export function graphql(
  source: 'fragment CartItems on CartItem {\n name\n quantity\n }\n '
): (typeof documents)['\n fragment CartItem on CartItem {\n name\n quantity\n }\n ']
export function graphql(
  source: 'query GetCartById($id: ID!) {\n cart(id: $id) {\nid\nitems {\nid\n...CartItem\n}\ntotalItems\n }\n }\n'
): (typeof documents)['\n query GetCartById($id: ID!) {\n cart(id: $id) {\n id\n items {\n id \n ...CartItem \n } \n totalItems \n } \n } n']
export function graphql(source: string): unknown
export function graphql(source: string) {
  // Here we access the documents object, so the bundler cannot make assumption over what is
  //  actually used at runtime
  return (documents as any)[source] ?? {}
}
```

Every time you use the `graphql` function, all the operations in the project are imported,
regardless of wether they are used or not. This can be a problem if you have a lot of documents
(query, mutation, and fragments) in your project.

<Callout>
  For more information/ a history of this functionality, you can read more about it in this blog
  post from Laurin Quast about this new client-preset feature: [Unleash the Power of Fragments with
  GraphQL Code
  Generator](https://the-guild.dev/blog/unleash-the-power-of-fragments-with-graphql-codegen). It was
  based on the former
  [`gql-tag-operations-preset`](https://the-guild.dev/graphql/codegen/plugins/presets/gql-tag-operations-preset)
  that is now replaced by the `client-preset`, which was originally explored by [Maël
  Nison](https://github.com/arcanis) in [this Github
  project](https://github.com/arcanis/graphql-typescript-integration).
</Callout>

## The Solution: A Compiler Plugin

This is where a compiler can make a big difference. Instead of using the map which contains all
GraphQL operations in the project, we can use the generated document types for that specific
operation. So the example above would compile to:

```ts
import { CartItemFragmentDoc } from './graphql'
import { GetCartByIdDocument } from './graphql'
const GetCartByIdDocument = GetCartByIdDocument
const CartItemFragment = CartItemFragmentDoc
```

There was already a
[Babel plugin](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#babel-plugin) for
the `client-preset` for projects using [Babel](https://babeljs.io) in the `client-preset` package.
Now, as SWC itself, and Next.js is becoming more popular and uses [SWC](https://swc.rs) as its
default compiler, there was a need for a SWC plugin as well. SWC is a fast and modern
JavaScript/TypeScript compiler written in Rust, so the Babel plugin couldn't be used.

This is where the new
[@graphql-codegen/client-preset-swc-plugin](https://www.npmjs.com/package/@graphql-codegen/client-preset-swc-plugin)
comes in. This plugin uses the [SWC](https://swc.rs) compiler to optimize the generated code,
reducing the bundle size for all your SWC projects!

## How to Use the SWC Plugin

To use the SWC plugin, you need to install the plugin:

```bash
npm install -D @graphql-codegen/client-preset-swc-plugin
yarn add -D @graphql-codegen/client-preset-swc-plugin
```

To use the SWC plugin with Next.js, update your `next.config.js` to add the following:

```js filename="next.config.js"
const nextConfig = {
  // ...
  experimental: {
    swcPlugins: [
      [
        '@graphql-codegen/client-preset-swc-plugin',
        { artifactDirectory: './src/gql', gqlTagName: 'graphql' }
      ]
    ]
  }
}
```

or with Vite React, update your `vite.config.ts` to add the following:

```ts filename="vite.config.ts"
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react({
      plugins: [
        [
          '@graphql-codegen/client-preset-swc-plugin',
          { artifactDirectory: './src/gql', gqlTagName: 'graphql' }
        ]
      ]
    })
  ]
})
```

or for other SWC projects add this to your `.swcrc` file:

```json5 filename=".swcrc"
{
  // ...
  jsc: {
    // ...
    experimental: {
      plugins: [
        [
          '@graphql-codegen/client-preset-swc-plugin',
          { artifactDirectory: './src/gql', gqlTagName: 'graphql' }
        ]
      ]
    }
  }
}
```

Then you're ready to go! The plugin will automatically optimize your generated code when SWC
compiles your files.

In conclusion, using the
[`client-preset`](https://graphql-code-generator.com/plugins/presets/client-preset) for GraphQL Code
Generator is a powerful way to improve the DX of your project. However, without proper optimization,
the bundle size can quickly become bloated. By using the
[@graphql-codegen/client-preset-swc-plugin](https://www.npmjs.com/package/@graphql-codegen/client-preset-swc-plugin),
(or the
[Babel plugin](https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#babel-plugin))
you can optimize the generated code and reduce the bundle size, and in the end improve the loading
time of your application.

Like every open source project we maintain, we always use it ourselves in production - in our own
products and with our clients. In
[Hive, our open source GraphQL Schema Registry](https://the-guild.dev/graphql/hive) we also use this
new plugin and [on this PR](https://github.com/kamilkisiela/graphql-hive/pull/2063) you can see the
changes we've made to introduce it and the benefits we got from it. Feel free to explore
[Hive's source code](https://github.com/kamilkisiela/graphql-hive) to find how
[The Guild is using GraphQL](https://www.youtube.com/watch?v=78ufgsWfgVc\&list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd\&index=3\&t=1081)