nitrogql logonitrogql

Migrating from GraphQL Code Generator

GraphQL Code Generator is a great tool for generating code from GraphQL schema and operations. Basically it has the same goal as nitrogql. This page guides you how to migrate from GraphQL Code Generator to nitrogql.

GraphQL Code Generator has a couple of presets that control how TypeScript code is generated. nitrogql's approach is similar to the near-operation-file preset. This preset was the recommended preset while GraphQL Code Generator was in v2.

While GraphQL Code Generator has changed their recommended preset to the client preset, nitrogql still endorses the idea of the near-operation-file preset.

๐Ÿ‘Œ nitrogql supports code generation for both client-side TypeScript code (code that uses GraphQL clients) and server-side TypeScript code (code that implements GraphQL resolvers). This guide covers migration of both client-side and server-side code.

Prerequisites

This guide assumes that you are using GraphQL Code Generator under the following conditions:

If you diverge from these conditions, you need to first migrate to these conditions before migrating to nitrogql.

Before migrating to nitrogql

Apart from the above fundamental differences, nitrogql has limited, opinionated set of configuration options. This means that some of the configuration options you used in GraphQL Code Generator may not be available in nitrogql.

We recommend you to first adjust your GraphQL Code Generator configuration to be compatible with nitrogql as much as possible. This will make the migration process easier.

Use TypedDocumentNode

GraphQL Code Generator has a couple of plugins that generate TypeScript code from GraphQL operations. For example, typescript-react-apollo generates React Hooks for each GraphQL operation which use Apollo Client under the hood.

However, nitrogql only supports TypedDocumentNode-based code generation. Don't worry, TypedDocumentNode can be used with any popular UI library or GraphQL client library. That's why GraphQL Code Generator also recommends using TypedDocumentNode.

Therefore, you need to migrate to the typed-document-node plugin. If you are not familiar with TypedDocumentNode, Episode #41 of graphql.wtf is a great resource to learn how to migrate to typed-document-node.

Before you can migrate to nitrogql, you need to be using only typescript-operations and typed-document-node plugins for client-side code, not those library-specific ones.

Regarding server-side code (if any), you should be using the typescript-resolvers plugin.

Disable case conversion

Under the default settings, GraphQL Code Generator converts identifiers to PascalCase. For example, getUser is converted to GetUser and ENUM_VALUE is converted to EnumValue.

nitrogql does not do such case conversion by default. Therefore, the namingConvention option of the typescript-operations plugin should be set to keep. If you change the namingConvention option, you may also need to change TypeScript code accordingly.

# codegen.yml
config:
  namingConvention: keep

Set enumsAsConst: true

GraphQL Code Generator generates code from enums using TypeScript's enum syntax by default. However, nitrogql does not use that syntax. Instead, nitrogql uses plain union types.

This difference is not a big deal, but it may cause some incompatibility issues. Therefore, it is recommended to set enumsAsConst: true and solve any incompatibility issues before migrating to nitrogql.

# codegen.yml
config:
  enumsAsConst: true

Change output extension

By default, the near-operation-file preset generates foo.generated.ts next to foo.graphql. This means that if you want to import code generated from foo.graphql, you need to import foo.generated.ts:

// default setting of GraphQL Code Generator
import { fooQuery } from "./foo.generated";

On the other hand, nitrogql recommends to import directly from foo.graphql:

// after migrating to nitrogql
import { fooQuery } from "./foo.graphql";

For the ease of migration, adjust GraphQL Code Generator's configuration to generate foo.graphql.ts instead of foo.generated.ts. This can be done by setting extension: .graphql.ts in presetConfig.

After you change the extension, you need to update all import declarations to import from .graphql files instead of .generated files. Don't forget to update your.gitignore to ignore .graphql.ts files.

# codegen.yml
generates:
  src/:
    # ...
    presetConfig:
      extension: .graphql.ts

Adjust generated type names

GraphQL Code Generator and nitrogql have different naming conventions for generated types. Before migrating to nitrogql, adjust your code to match nitrogql's naming convention.

For example, when you have a query named GetUser, default output of GraphQL Code Generator and nitrogql are summarized as follows:

GraphQL Code Generatornitrogql
operation document objectGetUserDocumentGetUserQuery
operation result typeGetUserQueryGetUserResult
operation variables typeGetUserQueryVariablesGetUserVariables

Note that Query in the table is substituted with Mutation or Subscription depending on the operation type.

You can adjust the names of result type and variables type with the following settings:

# GraphQL Code Generator config
config:
  omitOperationSuffix: true
  operationResultSuffix: Result

As is the case with other configuration changes, you need to update all TypeScript code that imports these types.

GraphQL Code Generator (adjusted)nitrogql
operation document objectGetUserDocumentGetUserQuery
operation result typeGetUserResultGetUserResult
operation variables typeGetUserVariablesGetUserVariables

Name of the operation document object (GetUserDocument) still differ from nitrogql with the above setting. Since GraphQL Code Generator cannot exactly match the nitrogql behavior, we will guide you to configure nitrogql to match the resulting behavior of GraphQL Code Generator.

Migrating to nitrogql (applies to both client-side and server-side code)

Now it's time to migrate to nitrogql!

๐Ÿคฏ If you are in a monorepo setting, adjust below instructions as explained in the monorepo guide.

Install nitrogql

First, install nitrogql and its dependencies.

npm install -D @nitrogql/cli

If you are using webpack, you also need to install appropriate webpack loader. Note that this also applies to Next.js projects.

npm install -D @nitrogql/graphql-loader

If you are using Rollup, you need to install appropriate Rollup plugin. Note that this also applies to Vite projects.

npm install -D @nitrogql/rollup-plugin

Create nitrogql config

nitrogql's configuration file is either graphql.config.yaml or .graphqlrc.yaml at the root of your project. .js or .ts files are supported too. You might have already one depending on your GraphQL Code Generator configuration. Also, you can use .json or .js files instead of .yaml at your preference.

You can reuse schema and documents options from your GraphQL Code Generator configuration. Start by copying them to your nitrogql configuration file:

# graphql.config.yaml
schema: src/schema/*.graphql
documents: src/app/**/*.graphql

Note that any other nitrogql options are put under extensions.nitrogql object.

Configure schema output

One option you need to set is generate.schemaOutput. This option controls where the generated schema type definition is written to. Set it to the path to the file where you want to write the schema type definition to. This option corresponds to the typescript plugin of GraphQL Code Generator.

In nitrogql, the schema type definition file will be depended by other generated file (both client-side and server-side).

Also, if you are importing enums from the generated schema file, you need to set generate.emitSchemaRuntime to true. This is the default setting of GraphQL Code Generator, but nitrogql does not emit runtime enum definitions by default.

Example:

# GraphQL Code Generator configuration
generates:
  path/to/schema.ts:
    plugins:
      - typescript
    config:
      # ...

# corresponding nitrogql configuration (graphql.config.yaml)
schema: src/schema/*.graphql
documents: src/app/**/*.graphql
extensions:
  nitrogql:
    generate:
      schemaOutput: path/to/schema.ts
      emitSchemaRuntime: true

Configure scalar types

If you have a customized mapping from GraphQL scalar types to TypeScript types, you need to migrate it to nitrogql's scalarTypes option.

Migration from GraphQL Code Generator's scalars option is straightforward. For example, if you have the following GraphQL Code Generator configuration:

# GraphQL Code Generator configuration
scalars:
  ID:
    input: string
    output: string | number
  DateTime: Date

You can migrate to nitrogql's scalarTypes option as follows:

# corresponding nitrogql configuration (graphql.config.yaml)
extensions:
  nitrogql:
    generate:
      # ...
      type:
        scalarTypes:
          ID:
            send: string | number
            receive: string
          DateTime: Date

Note that nitrogql's scalarTypes option takes send and receive properties instead of input and output properties. Read more about the difference in the Configuring Scalar Types page.

Migrating client-side to nitrogql

If you are using GraphQL Code Generator for client-side code, belows steps apply.

Configure operation output

Next, you need to configure generation of TypeScript code from GraphQL operations. This corresponds to the typescript-operations plugin of GraphQL Code Generator.

Without additional configuration, nitrogql generates TypeScript code next to each GraphQL operations files. This is the same architecture as GraphQL Code Generator's near-operation-file preset.

However, you need to adjust nitrogql's generate option so that you can use the generated code from your application in the same way as you did with GraphQL Code Generator.

Below is the nitrogql configuration for keeping the same behavior as your current settings.

schema: src/schema/*.graphql
documents: src/app/**/*.graphql
extensions:
  nitrogql:
    generate:
      schemaOutput: path/to/schema.ts
      emitSchemaRuntime: true
      # add below
      export:
        defaultExportForOperation: false
        variablesType: true
        operationResultType: true
      name:
        queryVariableSuffix: Document
        mutationVariableSuffix: Document
        subscriptionVariableSuffix: Document 

Configure TypeScript

In order to use the generated code from your application, you might need to adjust TypeScript configuration to recognize the generated code.

In your tsconfig.json, set the allowArbitraryExtensions compiler option to true so that TypeScript lets you import .graphql files.

Note that this option is only available in TypeScript 5.0 or later. If you are using an older version of TypeScript, you can set nitrogql's generate.mode option to with-loader-ts-4.0.

Configure webpack loader or Rollup plugin

As the last step, you need to configure webpack loader or Rollup plugin so that they can load .graphql files.

If you are using webpack, add the following to your webpack configuration:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      // ...
      {
        test: /\.graphql$/,
        use: [
          {
            loader: "@nitrogql/graphql-loader",
            options: {
              // path to your nitrogql configuration file
              configFile: "./graphql.config.yaml",
            },
          },
        ],
      },
    ],
  },
};

If you are using Rollup, add the following to your Rollup configuration:

// rollup.config.js
import graphql from "@nitrogql/rollup-plugin";

export default {
  // ...
  plugins: [
    // ...
    graphql({
      // path to your nitrogql configuration file
      configFile: "./graphql.config.yaml",
      include: ["**/*.graphql"],
    }),
  ],
};

Migrating server-side to nitrogql

For server-side code, the role of GraphQL Code Generator is to generate type definitions for resolvers. nitrogql also supports generating such type definitions.

Configure resolver output

First of all, you need to configure generation of resolver type definitions file. This is done by setting the generate.resolverOutput option.

Example:

# GraphQL Code Generator configuration
generates:
  src/generated/resolvers.ts:
    plugins:
      - typescript-resolvers
    config:
      # ...

# corresponding nitrogql configuration (graphql.config.yaml)
schema: src/schema/*.graphql
documents: src/app/**/*.graphql
extensions:
  nitrogql:
    generate:
      # ...
      resolverOutput: src/generated/resolvers.ts

Apply the model plugin

nitrogql has mechanism for generating significantly more type-safe definitions for resolvers. In return for this, use of the model plugin is almost a must.

First, add the model plugin to your nitrogql configuration:

extensions:
  nitrogql:
    plugins:
      - "nitrogql:model-plugin"
    # ...

Then, apply the @model directive to your GraphQL schema.

If you are using GraphQL Code Generator's mappers option, migration to nitrogql's model plugin is straightforward.

# GraphQL Code Generator configuration
generates:
  src/generated/resolvers.ts:
    config:
      # ...
      mappers:
        User: "~/models/User#User"
        
# nitrogql equivalent
type User @model(type: "import('~/models/User').User") {
  # ...
}

Otherwise, you should annotate each object type field with @model directive. The rule is that if you implement a resolver for a field, you do not need the @model directive. Any field that does not have a resolver implementation (i.e. a field that is resolved by the default resolver) needs the @model directive. Example:

type User {
  id: ID! @model
  name: String! @model
  email: String! @model
  posts: [Post!]!
}

Usage of generated resolver types

nitrogql's approach to generating resolver types is a little different from GraphQL Code Generator's.

GraphQL Code Generator outputs one type per GraphQL type. For example, if you have type Query and type User in your GraphQL schema, you get QueryResolvers and UserResolvers type generated by the typescript-resolvers plugin.

nitrogql employs a different approach. It generates one type named Resolvers that contains all information about resolvers. GraphQL Code Generator's UserResolvers corresponds to nitrogql's Resolvers<Context>["User"].

// GraphQL Code Generator
import type { Resolvers, UserResolvers } from "~/generated/resolvers";
const userResolvers: UserResolvers = {
  // ...
};

const resolvers: Resolvers = {
  User: userResolvers,
  // ...
};

// nitrogql
import type { Resolvers } from "~/generated/resolvers";
const userResolvers: Resolvers<Context>["User"] = {
  // ...
};

const resolvers: Resolvers<Context> = {
  User: userResolvers,
  // ...
};

The goal of your resolver implementation should be the same as before migration. You should make a resolvers object that contains all resolver implementations and pass it to your GraphQL server. If resolvers has type Resolvers<Context>, it should be safe in a sense that all resolver implementations have correct type and all required fields are implemented.

Note that there is another difference between GraphQL Code Generator and nitrogql in the handling of Context. A context is additional data that is passed to all resolver implementations. In GraphQL Code Generator, you can specify the type of context in the typescript-resolvers plugin's contextType option so the exported Resolvers type includes the context type. However, nitrogql does not have such option. Instead, nitrogql's Resolvers type is a generic type that takes the context type as a type argument.

Therefore, you need to change the type of resolvers to Resolvers<Context> to match the type of Resolvers generated by GraphQL Code Generator. If it is a problem for you, you can make an intermediate module that exports Resolvers<Context> and import it from your resolver implementation module.

Using Server GraphQL File

This is not a mandatory step, but you can also take advantage of Server GraphQL File to make your GraphQL server implementation easier. If you are using file system operations to load your GraphQL schema, this file is a great alternative.

Using nitrogql CLI

After you migrate to nitrogql, you need to also migrate build scripts to use nitrogql CLI.

Basically, you need to replace graphql-codegen command with nitrogql generate.

Watch mode

nitrogql CLI does not have a watch mode for now. If you need a watch mode, you can use nodemon or chokidar-cli to watch GraphQL files and run nitrogql generate automatically.

For example, if you are using chokidar-cli, a command for watching GraphQL files and running nitrogql generate is as follows:

chokidar '**/*.graphql' --initial --command 'npx nitrogql generate'

๐Ÿงบ Read Next: Configuration, CLI Usage