Zod
The @zenstackhq/zod package generates Zod validation schemas from your ZModel definitions. It provides type-safe schemas for models, embedded types, and enums, with full support for validation attributes and custom validation rules.
Installation​
- npm
- pnpm
- bun
- yarn
npm install @zenstackhq/zod zod
pnpm add @zenstackhq/zod zod
bun add @zenstackhq/zod zod
yarn add @zenstackhq/zod zod
Zod 4.0 or above is required.
API​
The package exports a createSchemaFactory function that takes a ZenStack schema as input and returns a factory for creating Zod schemas.
import schema from './zenstack/schema';
import { createSchemaFactory } from '@zenstackhq/zod';
const factory = createSchemaFactory(schema);
The factory exposes the following methods:
-
makeModelSchemaCreates a schema for the full shape of a model, including relation fields (as optional fields).
-
makeModelCreateSchemaCreates a schema for creating new records, with fields that have defaults being optional. The result schema excludes relation fields.
-
makeModelUpdateSchemaCreates a schema for updating records, with all fields being optional. The result schema excludes relation fields.
-
makeTypeSchemaCreates a schema for a Custom Type.
-
makeEnumSchemaCreates a schema for an enum type.
Schema Features​
The created Zod schemas have the following features:
- They are strongly typed.
- They verify the basic shapes of input args (object fields and types).
- They respect the additional validation attributes like
@email,@length, etc., as described in Input Validation. - If a ZModel declaration has a
@@metaor@metaattribute with "description" key, the meta value will be used as the Zod schema's metadata.
Samples​
// This is a sample model to get you started.
datasource db {
provider = 'sqlite'
}
/// User role
enum Role {
USER
ADMIN
}
/// Profile type definition
type Profile {
bio String?
website String? @url
}
/// User model
model User {
id Int @id @default(autoincrement())
email String @unique @email @meta('description', 'The unique email address of the user')
role Role @default(USER)
profile Profile? @json
posts Post[]
@@meta('description', 'A registered user of the application')
}
/// Post model
model Post {
id Int @id @default(autoincrement())
title String
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
import { createSchemaFactory } from '@zenstackhq/zod';
import { schema } from './zenstack/schema';
const factory = createSchemaFactory(schema);
// enum
const roleSchema = factory.makeEnumSchema('Role');
console.log('"STAFF" is a valid Role?', roleSchema.safeParse('STAFF').success);
// type
const profileSchema = factory.makeTypeSchema('Profile');
console.log(
'Profile allows invalid website?',
profileSchema.safeParse({ website: 'not-a-url' }).success,
);
// User model's create schema
const userCreateSchema = factory.makeModelCreateSchema('User');
console.log(
'User create schema allows omitting "role"?',
userCreateSchema.safeParse({ email: 'alice@example.com' }).success,
);
// User model full schema
const userSchema = factory.makeModelSchema('User');
console.log(
'User full model allows relations?',
userSchema.safeParse({
id: 1,
email: 'user@example.com',
role: 'ADMIN',
profile: { bio: 'A developer', website: 'https://example.com' },
posts: [{ id: 1, title: 'Post1' }],
}).success,
);
// `@@meta` and `@meta` handling
console.log(
'User schema has description meta:',
userSchema.meta()?.description,
);
console.log(
'User.email field has description:',
userCreateSchema.shape.email.meta()?.description,
);