DEV Community

Cover image for Optimize your Bundle Size with SWC and GraphQL Codegen
TheGuildBot for The Guild

Posted on • Updated on • Originally published at the-guild.dev

Optimize your Bundle Size with SWC and GraphQL Codegen

This article was published on Tuesday, April 18, 2023 by Jesse van der Velden @ The Guild Blog

With the new
client-preset for GraphQL Code Generator
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 */}

// 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!
Enter fullscreen mode Exit fullscreen mode

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
plugin for your applications supported by the SWC compiler such as projects using
Next.js or Vite.

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.

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] ?? {}
}
Enter fullscreen mode Exit fullscreen mode

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.


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
. It was
based on the former
gql-tag-operations-preset
that is now replaced by the client-preset, which was originally explored by Maël
Nison
in this Github
project
.

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:

import { CartItemFragmentDoc } from './graphql'
import { GetCartByIdDocument } from './graphql'
const GetCartByIdDocument = GetCartByIdDocument
const CartItemFragment = CartItemFragmentDoc
Enter fullscreen mode Exit fullscreen mode

There was already a
Babel plugin for
the client-preset for projects using Babel in the client-preset package.
Now, as SWC itself, and Next.js is becoming more popular and uses SWC 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
comes in. This plugin uses the SWC 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:

npm install -D @graphql-codegen/client-preset-swc-plugin
yarn add -D @graphql-codegen/client-preset-swc-plugin
Enter fullscreen mode Exit fullscreen mode

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' }
        ]
      ]
    })
  ]
})
Enter fullscreen mode Exit fullscreen mode

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)

Enter fullscreen mode Exit fullscreen mode

Top comments (0)