GraphQL Code Generator Typed Module Declarations
This `graphql-code-generator` plugin enhances the standard `@graphql-codegen/typescript-graphql-files-modules` plugin by generating TypeScript module declarations for `.graphql` files, but critically, it produces *typed* `DocumentNode` objects. This typing leverages the output of the `@graphql-codegen/typed-document-node` plugin, ensuring type-safety and auto-completion when importing GraphQL operations (queries, mutations, subscriptions) directly from `.graphql` files into your TypeScript codebase. It currently stands at version `1.0.3` and shows a stable release cadence with dependency updates and minor bug fixes. Key differentiators include its focus on generating fully typed `DocumentNode` definitions for imported GraphQL files, allowing for robust type-checking at compile time, which is not directly provided by `graphql-tag/loader` alone without this intermediary type generation. It also supports `.graphql` files with multiple operations, naming imports according to the operation names, enhancing code organization and readability.
Common errors
-
TypeScript error: Module './my-fragment.graphql' has no exported member 'MyFragment'.
cause You are attempting to import a GraphQL fragment using a named import, but this plugin generates fragments as default exports.fixChange your import statement for fragments to use a default import, e.g., `import MyFragment from './my-fragment.graphql';` -
TypeScript error: Property 'user' does not exist on type 'DocumentNode<any, Record<string, any>>'.
cause The generated `DocumentNode` is not being correctly typed by `TypedDocumentNode`, or the types are not being picked up when importing the `.graphql` file.fixVerify that `@graphql-codegen/typed-document-node` is correctly configured and runs before this plugin. Ensure the `typedDocumentNodeModule` path in this plugin's configuration in `codegen.yml` is correctly pointing to the output file of `typed-document-node`.
Warnings
- gotcha GraphQL fragments defined in `.graphql` files are generated as *default* imports, not named imports, when consumed as modules. Attempting to use a named import for a fragment will lead to a TypeScript error or runtime failure.
- gotcha This plugin is dependent on the `@graphql-codegen/typed-document-node` plugin. It must be configured to run *before* this plugin in your `codegen.yml`, and the `typedDocumentNodeModule` option in this plugin's configuration must accurately point to the output file of the `typed-document-node` plugin. Incorrect setup will result in missing or incorrect type information.
Install
-
npm install codegen-typescript-graphql-module-declarations-plugin -
yarn add codegen-typescript-graphql-module-declarations-plugin -
pnpm add codegen-typescript-graphql-module-declarations-plugin
Imports
- codegen.yml configuration
# codegen.yml plugins: - codegen-typescript-graphql-module-declarations-plugin
- MyQuery
import { MyQuery } from './my-query.graphql'; - UserFields
import { UserFields } from './my-fragment.graphql';import UserFields from './my-fragment.graphql';
Quickstart
/* package.json */
{
"name": "my-graphql-app",
"version": "1.0.0",
"devDependencies": {
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/typescript": "^4.0.0",
"@graphql-codegen/typescript-operations": "^4.0.0",
"@graphql-codegen/typed-document-node": "^5.0.0",
"codegen-typescript-graphql-module-declarations-plugin": "^1.0.0",
"graphql": "^16.0.0"
}
}
/* src/my-query.graphql */
query MyQuery {
user {
id
name
}
}
/* src/my-fragment.graphql */
fragment UserFields on User {
id
name
}
/* codegen.yml */
overwrite: true
schema: "http://localhost:4000/graphql" # Replace with your GraphQL API endpoint
documents: "src/**/*.graphql"
generates:
src/graphql/generated.ts:
plugins:
- typescript
- typescript-operations
- typed-document-node
src/graphql/modules.d.ts:
plugins:
- codegen-typescript-graphql-module-declarations-plugin
config:
typedDocumentNodeModule: ./generated # Relative path from modules.d.ts to generated.ts
/* src/app.ts */
// Run `npx graphql-codegen` first to generate types
// Then `tsc src/app.ts` to type-check
import { MyQuery } from './my-query.graphql';
import UserFields from './my-fragment.graphql'; // Fragments are default imports
interface User { id: string; name: string; }
// MyQuery is a TypedDocumentNode with inferred types
// You would typically pass this to a GraphQL client (e.g., Apollo Client's useQuery)
console.log('MyQuery operation name:', MyQuery.definitions[0].name?.value); // Should log 'MyQuery'
// Example of accessing the inferred types (runtime code would use actual client data)
type MyQueryResult = typeof MyQuery['__generated_graphql_codegen_typescript_document_node']['result'];
const simulatedQueryResult: MyQueryResult = {
user: { id: 'user123', name: 'Alice' }
};
console.log('Simulated query result:', simulatedQueryResult.user?.name);
// UserFields is a DocumentNode, its types are also inferred for consistency
console.log('UserFields fragment name:', UserFields.definitions[0].name?.value); // Should log 'UserFields'
// A fragment doesn't have a direct 'result' type like a query, but its fields are known
// type UserFieldsType = typeof UserFields['__generated_graphql_codegen_typescript_document_node']; // This approach is for operations
// Instead, use the type generated by typescript-operations for the fragment
interface UserFieldsType { id: string; name: string; }
const simulatedFragmentData: UserFieldsType = { id: 'frag456', name: 'Bob' };
console.log('Simulated fragment data:', simulatedFragmentData.name);