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/cliThen, 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 --runExternalCodeIn 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
- The content mapper only provides types. You still need a loader for your bundler so that
.graphqlfiles work at runtime. - Generated files for GraphQL servers (
resolversOutputandserverGraphqlOutput) are still generated bynitrogql generate. - After migrating, you can delete previously generated
.d.graphql.tsfiles. TheallowArbitraryExtensionscompiler option is not needed for the content mapper. - When TypeScript reports GraphQL errors, it skips reporting type errors in TypeScript code until those are fixed, as it does for syntax errors.
