GraphQL Code Generator plugin that generates validation schemas from your GraphQL schema.
GraphQL Code Generator's docs now lead with codegen.ts. YAML still works, but new examples here use TypeScript config so plugin options can be typed and kept close to the plugin that consumes them.
npm i graphql zod
npm i -D @graphql-codegen/cli @graphql-codegen/typescript graphql-codegen-typescript-validation-schemaThe example below uses zodv4. If you use another validator, install that package instead and change schema to yup, zod, myzod, or valibot. The generated schema code imports the matching library, so lying here will fail in the least interesting way.
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: './schema.graphql',
generates: {
'./src/graphql.ts': {
plugins: [
'typescript',
{
'typescript-validation-schema': {
schema: 'zodv4',
scalarSchemas: {
DateTime: 'z.string().datetime()',
},
},
},
],
config: {
strictScalars: true,
scalars: {
ID: {
input: 'string',
output: 'string',
},
DateTime: {
input: 'string',
output: 'string',
},
},
},
},
},
};
export default config;Run Codegen with:
npx graphql-codegen --config codegen.tsThe scalars.ID mapping is worth keeping even though it feels boring. It makes the generated TypeScript type for ID line up with the validation schema. See #375 for the original footgun.
YAML configuration remains supported. Existing projects can keep it, and the old style examples live in docs/yaml-configuration.md.
For larger examples, see the example directory. Each schema package has extra notes in its own example README.
GraphQL Code Generator accepts config at the root, output, and plugin level. For this plugin, put validation-specific options on the typescript-validation-schema plugin entry when possible:
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: './schema.graphql',
generates: {
'./src/validation.ts': {
plugins: [
{
'typescript-validation-schema': {
schema: 'zodv4',
scalarSchemas: {
DateTime: 'z.string().datetime()',
},
},
},
],
config: {
scalars: {
ID: {
input: 'string',
output: 'string',
},
DateTime: {
input: 'string',
output: 'string',
},
},
},
},
},
};
export default config;Shared options such as scalars, strictScalars, typesPrefix, or enumsAsTypes can still live at output level when they should apply to both typescript and this plugin.
For client apps, GraphQL Code Generator recommends the client preset. Install the preset, then generate client artifacts and validation schemas as separate outputs:
npm i -D @graphql-codegen/client-presetimport type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: './schema.graphql',
documents: ['src/**/*.{graphql,ts,tsx}'],
generates: {
'./src/gql/': {
preset: 'client',
},
'./src/validation.ts': {
plugins: [
{
'typescript-validation-schema': {
schema: 'zodv4',
importFrom: './gql/graphql',
scalarSchemas: {
DateTime: 'z.string().datetime()',
},
},
},
],
config: {
scalars: {
ID: {
input: 'string',
output: 'string',
},
DateTime: {
input: 'string',
output: 'string',
},
},
},
},
},
};
export default config;This avoids relying on preset internals to pass a shared config object through to every plugin.
All snippets below are values for the typescript-validation-schema plugin config object unless the example shows a full CodegenConfig.
type: ValidationSchema default: 'yup'
Selects the validation library.
{
schema: 'yup',
}Supported values are yup, zod, zodv4, myzod, and valibot.
type: string default: 'zod'
Sets a custom import path for Zod. This only applies when schema is zod.
{
schema: 'zod',
zodImportPath: 'zod/v3',
}Use this if your project uses Zod v4 but wants this plugin's Zod v3-compatible output.
type: string
Imports GraphQL TypeScript types from another generated file. If you skip this option, the generator omits the type import.
import type { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: './schema.graphql',
generates: {
'./src/graphql.ts': {
plugins: ['typescript'],
},
'./src/validation.ts': {
plugins: [
{
'typescript-validation-schema': {
schema: 'zodv4',
importFrom: './graphql',
},
},
],
},
},
};
export default config;Generated code imports from that file:
import { GeneratedInput } from './graphql'type: string
Uses a namespace import from importFrom instead of one named import per type.
{
schema: 'yup',
importFrom: './graphql',
schemaNamespacedImportName: 'types',
}Generated code:
import * as types from './graphql'type: boolean default: false
Uses import type {} when importing generated TypeScript types. Use it with importFrom.
{
schema: 'zodv4',
importFrom: './graphql',
useTypeImports: true,
}type: string default: ''
Prefixes imported generated type names.
{
schema: 'zodv4',
importFrom: './graphql',
typesPrefix: 'I',
}type: string default: ''
Suffixes imported generated type names.
{
schema: 'zodv4',
importFrom: './graphql',
typesSuffix: 'I',
}type: boolean default: false
Generates enum validation schemas for TypeScript string union types instead of TypeScript enum.
{
schema: 'zodv4',
enumsAsTypes: true,
}type: boolean default: false
Disallows empty strings in generated string schemas.
{
schema: 'yup',
notAllowEmptyString: true,
}type: ScalarSchemas
Overrides validation schemas for built-in scalars and custom GraphQL scalars.
{
schema: 'yup',
scalarSchemas: {
Date: 'yup.date()',
Email: 'yup.string().email()',
},
}{
schema: 'zodv4',
scalarSchemas: {
Date: 'z.date()',
Email: 'z.string().email()',
},
}{
schema: 'valibot',
scalarSchemas: {
Date: 'v.date()',
Email: 'v.pipe(v.string(), v.email())',
},
}type: string
Fallback validation schema for scalar types not listed in scalarSchemas.
{
schema: 'zodv4',
defaultScalarTypeSchema: 'z.unknown()',
}type: boolean default: false
Generates validation schemas for GraphQL object types. Query, Mutation, and Subscription are excluded.
{
schema: 'zodv4',
withObjectType: true,
}This option was added for projects that need simple object schemas. See #20 and #107.
withObjectType does not support fragment generation.
type: boolean default: false
Generates Zod schemas for GraphQL operation result selection sets. Use this when withObjectType is too broad because your query selects only part of an object.
{
schema: 'zodv4',
withOperationType: true,
}Currently supported for schema: 'zod' and schema: 'zodv4'.
type: number
Limits nested object validation depth for withObjectType schemas generated by schema: 'zod' and schema: 'zodv4'.
{
schema: 'zodv4',
withObjectType: true,
maxDepth: 1,
}Use this for cyclic output graphs where validating the whole object graph would recurse forever.
type: ValidationSchemaExportType default: 'function'
Controls whether schemas are exported as functions or constants.
{
schema: 'zodv4',
validationSchemaExportType: 'const',
}type: 'nullish' | 'nullable' | 'optional' default: 'nullish'
Controls how nullable GraphQL fields are generated for schema: 'zod' and schema: 'zodv4'.
{
schema: 'zodv4',
zodOptionalType: 'nullable',
}The default nullish mode matches GraphQL input coercion, where a nullable input field may be omitted or passed as null. Use nullable or optional only when your generated TypeScript Maybe or InputMaybe contract differs from GraphQL's default behavior.
nullishBehavior is accepted as an alias.
type: boolean default: false
Appends .strict() to generated Zod object schemas.
{
schema: 'zodv4',
strictObjectSchemas: true,
}type: boolean default: false
Appends .describe() to generated Zod fields and object schemas from GraphQL descriptions.
{
schema: 'zodv4',
withDescriptions: true,
}type: boolean default: false
Generates only enum validation schemas.
{
schema: 'zodv4',
onlyEnums: true,
}type: boolean default: false
Uses the enum type path as the default value instead of the stringified enum value.
{
schema: 'zodv4',
useEnumTypeAsDefaultValue: true,
}type: NamingConventionMap default: { enumValues: 'change-case-all#pascalCase' }
Controls generated names. This follows GraphQL Code Generator's namingConvention option.
{
schema: 'zodv4',
namingConvention: {
enumValues: 'change-case-all#upperCase',
},
}type: DirectiveConfig
Maps GraphQL directives to validation library APIs.
Given this schema:
input ExampleInput {
email: String! @required(msg: "Hello, World!") @constraint(minLength: 50, format: "email")
message: String! @constraint(startsWith: "Hello")
}Yup config:
{
schema: 'yup',
directives: {
required: {
msg: 'required',
},
constraint: {
minLength: 'min',
startsWith: ['matches', /^$1/],
format: {
email: 'email',
},
},
},
}Generated Yup schema:
export function ExampleInputSchema(): yup.ObjectSchema<ExampleInput> {
return yup.object({
email: yup.string().defined().required('Hello, World!').min(50).email(),
message: yup.string().defined().matches(/^Hello/)
})
}Zod config:
{
schema: 'zodv4',
directives: {
constraint: {
minLength: 'min',
startsWith: ['regex', /^$1/, 'message'],
format: {
email: 'email',
},
},
},
}Generated Zod schema:
export function ExampleInputSchema(): z.ZodObject<Properties<ExampleInput>> {
return z.object({
email: z.string().min(50).email(),
message: z.string().regex(/^Hello/, 'message')
})
}Other validation libraries use the same directive shape, but the API names differ. The generated examples in example/ and tests/directive.spec.ts are the best source when wiring a new directive. Annoying, but honest.