nitrogql logonitrogql

Using TypeScript Content Mapper

TypeScript 7.1 introduced content mappers, a plugin mechanism that lets TypeScript understand files other than TypeScript and JavaScript. nitrogql provides a content mapper for .graphql files. With it, TypeScript reads your GraphQL files directly and you no longer need to run nitrogql generate to get types for your operations.

🧪 Experimental: content mappers are a new feature of TypeScript 7.1, which is currently in beta. Because the feature may still change in TypeScript, nitrogql's content mapper is experimental too: its behavior and options may change in minor releases of nitrogql, including in ways that are not backward compatible. Generating types with nitrogql generate remains the stable way to use nitrogql. Feedback is welcome on GitHub Issues.

How It Works

Usually, nitrogql generate generates a .d.graphql.ts file next to each .graphql file, and TypeScript reads the generated file when you import a .graphql file.

With the content mapper, TypeScript asks nitrogql for the types of a .graphql file whenever it needs them. Nothing is written to the disk, so there is no generated file to keep up to date. Since the types always reflect the current content of your GraphQL files, editors show the result of your edits immediately.

Additionally, errors in your GraphQL files (syntax errors and validation errors against the schema) are reported by TypeScript as well. Each error has a code such as nitrogql2001; see Error codes for the list.

While a GraphQL file has errors, the operations and fragments it exports are typed as any, so that code importing the file does not get unrelated errors.

Setup

First, make sure you are using TypeScript 7.1 or later, and that @nitrogql/cli is installed.

npm install --save-dev typescript@beta @nitrogql/cli

Then, register nitrogql's content mapper in your tsconfig.json:

{
  "compilerOptions": {
    // ...
  },
  "contentMappers": [
    {
      "package": "@nitrogql/cli",
      "extensions": [".graphql"]
    }
  ]
}

Your nitrogql configuration file is used as usual. By default, the content mapper looks for a configuration file in the same directory as tsconfig.json. Note that the content mapper only needs the schema and documents options; you can remove the schemaOutput option if you only used it for operation types.

schema: ./schema/*.graphql
documents: ./app/**/*.graphql

Content mappers are external programs executed by TypeScript, so you need to explicitly allow running them. On the command line, pass the --runExternalCode flag to tsc:

npx tsc --runExternalCode

In VS Code, content mappers run when the workspace is trusted and the js/ts.contentMappers.enabled setting is enabled.

Schema Types

Types for operations depend on types generated from your schema. When your schema consists of GraphQL files, the content mapper provides schema types too, and no generated files are needed at all. Schema types are provided as the types of the first schema file, so you can even import schema types from that file:

import type { User } from "./schema/schema.graphql";

If your schema files have a different extension (such as .graphqls), add the extension to extensions in tsconfig.json as well.

Your schema may also include JavaScript/TypeScript schema files, such as a file that defines scalars with GraphQL Scalars. Their types, including scalar types given by plugins, are provided from the first GraphQL schema file as well. TypeScript loads the project again when a schema file changes, but not when a file imported by a JavaScript/TypeScript schema file changes.

When the schemaOutput option or the schemaModuleSpecifier option is set, operation types import schema types from there instead. This is required when none of your schema files is a GraphQL file, i.e. when your schema is given as an introspection JSON or only as JavaScript/TypeScript schema files, because the content mapper has no file to provide schema types from. In that case, keep running nitrogql generate to generate the schema type definition file.

Fragment Imports

Fragments imported with #import are not copied into the importing file. Instead, the types of the importing file import the fragment from the imported file, and TypeScript merges them into the types of operations. When you edit a fragment, TypeScript updates the files that import it, like any other module.

Because of this, the content mapper does not validate spreads of imported fragments, such as whether the imported file defines the fragment or whether its type condition can apply. Run nitrogql check (for example in CI) to validate them. Errors in a fragment are reported in the file that defines it; while the fragment has errors, selections that spread it are typed as any.

Merging is done by TypeScript's type checker, so operations that spread many imported fragments take more time to type-check than operations without them. The utility types for merging are provided by @nitrogql/cli, which therefore needs to be resolvable from your GraphQL files.

Options

The content mapper accepts options via the options field in tsconfig.json.

configFile

Path to the nitrogql configuration file, relative to tsconfig.json. Use this when your configuration file is not in the same directory as tsconfig.json, or has a non-default name.

"contentMappers": [
  {
    "package": "@nitrogql/cli",
    "extensions": [".graphql"],
    "options": {
      "configFile": "./config/graphql.config.ts"
    }
  }
]

Notes