graphql-codegen-typescript-validation-schema
Version:
GraphQL Code Generator plugin to generate form validation schema from your GraphQL schema
594 lines (449 loc) • 13.3 kB
Markdown
# graphql-codegen-typescript-validation-schema
[](https://github.com/Code-Hex/graphql-codegen-typescript-validation-schema/actions/workflows/ci.yml) [](https://badge.fury.io/js/graphql-codegen-typescript-validation-schema)
[GraphQL Code Generator](https://github.com/dotansimha/graphql-code-generator) plugin that generates validation schemas from your GraphQL schema.
- [x] [yup](https://github.com/jquense/yup)
- [x] [zod](https://github.com/colinhacks/zod)
- [x] [zod v4](https://github.com/colinhacks/zod) (`zodv4`)
- [x] [myzod](https://github.com/davidmdm/myzod)
- [x] [valibot](https://valibot.dev/)
## Quick start
GraphQL Code Generator's docs now lead with [`codegen.ts`](https://the-guild.dev/graphql/codegen/docs/config-reference/codegen-config). 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.
```sh
npm i graphql zod
npm i -D @graphql-codegen/cli @graphql-codegen/typescript graphql-codegen-typescript-validation-schema
```
The 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.
```ts
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:
```sh
npx graphql-codegen --config codegen.ts
```
The `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](https://github.com/Code-Hex/graphql-codegen-typescript-validation-schema/pull/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](./docs/yaml-configuration.md).
For larger examples, see the [example](https://github.com/Code-Hex/graphql-codegen-typescript-validation-schema/tree/main/example) directory. Each schema package has extra notes in its own example README.
## Where plugin config belongs
GraphQL Code Generator accepts config at the [root, output, and plugin level](https://the-guild.dev/graphql/codegen/docs/config-reference/config-field). For this plugin, put validation-specific options on the `typescript-validation-schema` plugin entry when possible:
```ts
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.
## Client preset
For client apps, GraphQL Code Generator recommends the [`client` preset](https://the-guild.dev/graphql/codegen/plugins/presets/client-preset). Install the preset, then generate client artifacts and validation schemas as separate outputs:
```sh
npm i -D @graphql-codegen/client-preset
```
```ts
import 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.
## Config API reference
All snippets below are values for the `typescript-validation-schema` plugin config object unless the example shows a full `CodegenConfig`.
### `schema`
type: `ValidationSchema` default: `'yup'`
Selects the validation library.
```ts
{
schema: 'yup',
}
```
Supported values are `yup`, `zod`, `zodv4`, `myzod`, and `valibot`.
### `zodImportPath`
type: `string` default: `'zod'`
Sets a custom import path for Zod. This only applies when `schema` is `zod`.
```ts
{
schema: 'zod',
zodImportPath: 'zod/v3',
}
```
Use this if your project uses Zod v4 but wants this plugin's Zod v3-compatible output.
### `importFrom`
type: `string`
Imports GraphQL TypeScript types from another generated file. If you skip this option, the generator omits the type import.
```ts
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:
```ts
import { GeneratedInput } from './graphql'
```
### `schemaNamespacedImportName`
type: `string`
Uses a namespace import from `importFrom` instead of one named import per type.
```ts
{
schema: 'yup',
importFrom: './graphql',
schemaNamespacedImportName: 'types',
}
```
Generated code:
```ts
import * as types from './graphql'
```
### `useTypeImports`
type: `boolean` default: `false`
Uses `import type {}` when importing generated TypeScript types. Use it with `importFrom`.
```ts
{
schema: 'zodv4',
importFrom: './graphql',
useTypeImports: true,
}
```
### `typesPrefix`
type: `string` default: `''`
Prefixes imported generated type names.
```ts
{
schema: 'zodv4',
importFrom: './graphql',
typesPrefix: 'I',
}
```
### `typesSuffix`
type: `string` default: `''`
Suffixes imported generated type names.
```ts
{
schema: 'zodv4',
importFrom: './graphql',
typesSuffix: 'I',
}
```
### `enumsAsTypes`
type: `boolean` default: `false`
Generates enum validation schemas for TypeScript string union types instead of TypeScript `enum`.
```ts
{
schema: 'zodv4',
enumsAsTypes: true,
}
```
### `notAllowEmptyString`
type: `boolean` default: `false`
Disallows empty strings in generated string schemas.
```ts
{
schema: 'yup',
notAllowEmptyString: true,
}
```
### `scalarSchemas`
type: `ScalarSchemas`
Overrides validation schemas for built-in scalars and custom GraphQL scalars.
```ts
{
schema: 'yup',
scalarSchemas: {
Date: 'yup.date()',
Email: 'yup.string().email()',
},
}
```
```ts
{
schema: 'zodv4',
scalarSchemas: {
Date: 'z.date()',
Email: 'z.string().email()',
},
}
```
```ts
{
schema: 'valibot',
scalarSchemas: {
Date: 'v.date()',
Email: 'v.pipe(v.string(), v.email())',
},
}
```
### `defaultScalarTypeSchema`
type: `string`
Fallback validation schema for scalar types not listed in `scalarSchemas`.
```ts
{
schema: 'zodv4',
defaultScalarTypeSchema: 'z.unknown()',
}
```
### `withObjectType`
type: `boolean` default: `false`
Generates validation schemas for GraphQL object types. `Query`, `Mutation`, and `Subscription` are excluded.
```ts
{
schema: 'zodv4',
withObjectType: true,
}
```
This option was added for projects that need simple object schemas. See [#20](https://github.com/Code-Hex/graphql-codegen-typescript-validation-schema/issues/20#issuecomment-1058969191) and [#107](https://github.com/Code-Hex/graphql-codegen-typescript-validation-schema/issues/107).
`withObjectType` does not support fragment generation.
### `withOperationType`
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.
```ts
{
schema: 'zodv4',
withOperationType: true,
}
```
Currently supported for `schema: 'zod'` and `schema: 'zodv4'`.
### `maxDepth`
type: `number`
Limits nested object validation depth for `withObjectType` schemas generated by `schema: 'zod'` and `schema: 'zodv4'`.
```ts
{
schema: 'zodv4',
withObjectType: true,
maxDepth: 1,
}
```
Use this for cyclic output graphs where validating the whole object graph would recurse forever.
### `validationSchemaExportType`
type: `ValidationSchemaExportType` default: `'function'`
Controls whether schemas are exported as functions or constants.
```ts
{
schema: 'zodv4',
validationSchemaExportType: 'const',
}
```
### `zodOptionalType`
type: `'nullish' | 'nullable' | 'optional'` default: `'nullish'`
Controls how nullable GraphQL fields are generated for `schema: 'zod'` and `schema: 'zodv4'`.
```ts
{
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.
### `strictObjectSchemas`
type: `boolean` default: `false`
Appends `.strict()` to generated Zod object schemas.
```ts
{
schema: 'zodv4',
strictObjectSchemas: true,
}
```
### `withDescriptions`
type: `boolean` default: `false`
Appends `.describe()` to generated Zod fields and object schemas from GraphQL descriptions.
```ts
{
schema: 'zodv4',
withDescriptions: true,
}
```
### `onlyEnums`
type: `boolean` default: `false`
Generates only enum validation schemas.
```ts
{
schema: 'zodv4',
onlyEnums: true,
}
```
### `useEnumTypeAsDefaultValue`
type: `boolean` default: `false`
Uses the enum type path as the default value instead of the stringified enum value.
```ts
{
schema: 'zodv4',
useEnumTypeAsDefaultValue: true,
}
```
### `namingConvention`
type: `NamingConventionMap` default: `{ enumValues: 'change-case-all#pascalCase' }`
Controls generated names. This follows GraphQL Code Generator's [`namingConvention`](https://the-guild.dev/graphql/codegen/docs/config-reference/naming-convention#namingconvention) option.
```ts
{
schema: 'zodv4',
namingConvention: {
enumValues: 'change-case-all#upperCase',
},
}
```
### `directives`
type: `DirectiveConfig`
Maps GraphQL directives to validation library APIs.
Given this schema:
```graphql
input ExampleInput {
email: String! @required(msg: "Hello, World!") @constraint(minLength: 50, format: "email")
message: String! @constraint(startsWith: "Hello")
}
```
Yup config:
```ts
{
schema: 'yup',
directives: {
required: {
msg: 'required',
},
constraint: {
minLength: 'min',
startsWith: ['matches', /^$1/],
format: {
email: 'email',
},
},
},
}
```
Generated Yup schema:
```ts
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:
```ts
{
schema: 'zodv4',
directives: {
constraint: {
minLength: 'min',
startsWith: ['regex', /^$1/, 'message'],
format: {
email: 'email',
},
},
},
}
```
Generated Zod schema:
```ts
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.