@zod Comment Annotations
Use triple-slash Prisma doc comments with @zod to append validations.
model User {
id String @id @default(cuid())
/// @zod.email().min(5)
email String @unique
}
Result:
export const UserSchema = z
.object({
email: z.string().email().min(5),
// ...
})
.strict();
Annotations are concatenated after base type; unsafe expressions are not executed (string append model). Keep rules pure.
Complete Feature Reference
String Validations
Length & Content Validation
model StringValidation {
id String @id @default(cuid())
/// @zod.min(2, "Too short")
name String
/// @zod.max(100)
title String
/// @zod.length(10)
code String
/// @zod.includes("@")
email String
/// @zod.startsWith("https://")
website String
/// @zod.endsWith(".com")
domain String
/// @zod.regex(/^[A-Z]+$/, "Must be uppercase")
acronym String
}
String Transformation
model StringTransform {
id String @id @default(cuid())
/// @zod.trim()
cleaned String
/// @zod.toLowerCase()
slug String
/// @zod.toUpperCase()
code String
/// @zod.uppercase()
acronym String
/// @zod.lowercase()
text String
/// @zod.normalize()
normalized String
}
Standard Format Validation
model StandardFormats {
id String @id @default(cuid())
/// @zod.email("Invalid email format")
email String @unique
/// @zod.url()
website String?
/// @zod.uuid()
reference String
/// @zod.datetime()
timestamp String
/// @zod.ip()
ipAddress String
/// @zod.cidr()
network String
/// @zod.date()
dateStr String
/// @zod.time()
timeStr String
/// @zod.duration()
period String
}
Zod v4 String Format Methods
The generator automatically detects your Zod version and uses optimized base types in v4.
Network & URL Formats
model NetworkFormats {
id String @id @default(cuid())
/// @zod.httpUrl()
apiUrl String
/// @zod.hostname()
server String
/// @zod.ipv4()
ipAddress String
/// @zod.ipv6()
ipv6Addr String?
/// @zod.cidrv4()
subnet String
/// @zod.cidrv6()
subnet6 String?
}
Generated schema (Zod v4):
export const NetworkFormatsCreateInputSchema = z.object({
apiUrl: z.httpUrl(), // Base type in v4
server: z.hostname(), // Base type in v4
ipAddress: z.ipv4(), // Base type in v4
ipv6Addr: z.ipv6().optional(),
subnet: z.cidrv4(),
subnet6: z.cidrv6().optional(),
});
Identifier Formats
model Identifiers {
id String @id @default(cuid())
/// @zod.guid()
guid String
/// @zod.nanoid()
nanoid String
/// @zod.cuid()
cuid String
/// @zod.cuid2()
cuid2 String
/// @zod.ulid()
ulid String
}
Generated schema (Zod v4):
export const IdentifiersCreateInputSchema = z.object({
guid: z.guid(), // Base type in v4
nanoid: z.nanoid(), // Base type in v4
cuid: z.cuid(), // Base type in v4
cuid2: z.cuid2(), // Base type in v4
ulid: z.ulid(), // Base type in v4
});
Encoding & Character Formats
model EncodingData {
id String @id @default(cuid())
/// @zod.base64()
base64 String
/// @zod.base64url()
base64url String
/// @zod.hex()
hex String
/// @zod.emoji()
reaction String
}
Generated schema (Zod v4):
export const EncodingDataCreateInputSchema = z.object({
base64: z.base64(), // Base type in v4
base64url: z.base64url(), // Base type in v4
hex: z.hex(), // Base type in v4
reaction: z.emoji(), // Base type in v4
});
Security & Crypto Formats
model SecurityData {
id String @id @default(cuid())
/// @zod.jwt()
token String?
/// @zod.hash("sha256")
checksum String
}
Generated schema (Zod v4):
export const SecurityDataCreateInputSchema = z.object({
token: z.jwt().optional(), // Base type in v4
checksum: z.hash("sha256"), // Base type with parameter
});
ISO Date/Time Formats
model ISOFormats {
id String @id @default(cuid())
/// @zod.isoDate()
date String
/// @zod.isoTime()
time String
/// @zod.isoDatetime()
datetime String
/// @zod.isoDuration()
duration String
}
Generated schema (Zod v4):
export const ISOFormatsCreateInputSchema = z.object({
date: z.iso.date(), // ISO methods use z.iso namespace
time: z.iso.time(),
datetime: z.iso.datetime(),
duration: z.iso.duration(),
});
Number Validations
model NumberValidation {
id String @id @default(cuid())
/// @zod.min(0, "Cannot be negative")
score Int
/// @zod.max(100)
percent Int
/// @zod.gt(0, "Must be greater than 0")
revenue Float
/// @zod.gte(0, "Cannot be negative")
assets Float
/// @zod.lt(100, "Must be less than 100")
discount Float
/// @zod.lte(100, "Cannot exceed 100")
capacity Float
/// @zod.step(0.01, "Must be in 0.01 increments")
price Float
/// @zod.positive("Must be positive")
amount Float
/// @zod.negative()
debt Float?
/// @zod.nonnegative()
balance Float
/// @zod.nonpositive()
loss Float?
/// @zod.int()
whole Float
/// @zod.finite()
measured Float
/// @zod.safe()
counter Int
/// @zod.multipleOf(5, "Must be multiple of 5")
rating Int
}
Generated schema:
export const NumberValidationCreateInputSchema = z.object({
score: z.number().int().min(0, "Cannot be negative"),
percent: z.number().int().max(100),
revenue: z.number().gt(0, "Must be greater than 0"),
assets: z.number().gte(0, "Cannot be negative"),
discount: z.number().lt(100, "Must be less than 100"),
capacity: z.number().lte(100, "Cannot exceed 100"),
price: z.number().multipleOf(0.01, "Must be in 0.01 increments"),
amount: z.number().positive("Must be positive"),
debt: z.number().negative().optional(),
balance: z.number().nonnegative(),
loss: z.number().nonpositive().optional(),
whole: z.number().int(),
measured: z.number().finite(),
counter: z.number().int().safe(),
rating: z.number().int().multipleOf(5, "Must be multiple of 5"),
});
Array Validations
model ArrayValidation {
id String @id @default(cuid())
/// @zod.min(1, "At least one item required")
tags String[]
/// @zod.max(10)
items String[]
/// @zod.length(3)
coords Float[]
/// @zod.nonempty()
colors String[]
/// @zod.nullable()
options String[]?
}
Generated schema:
export const ArrayValidationCreateInputSchema = z.object({
tags: z.string().array().min(1, "At least one item required"),
items: z.string().array().max(10),
coords: z.number().array().length(3),
colors: z.string().array().nonempty(),
options: z.string().array().nullable().optional(),
});
Date Validations
model DateValidation {
id String @id @default(cuid())
/// @zod.min(new Date('2020-01-01'))
startDate DateTime
/// @zod.max(new Date('2030-12-31'))
endDate DateTime
}
Field Modifiers
model FieldModifiers {
id String @id @default(cuid())
/// @zod.optional()
description String
/// @zod.nullable()
notes String?
/// @zod.nullish()
metadata String?
/// @zod.default("active")
status String
}
Advanced Modifiers
model AdvancedModifiers {
id String @id @default(cuid())
/// @zod.catch("fallback")
safeData String
/// @zod.pipe(z.string().transform(s => s.toUpperCase()))
processed String
/// @zod.brand<"UserId">()
userId String
/// @zod.readonly()
immutable String
}
Generated schema:
export const AdvancedModifiersCreateInputSchema = z.object({
safeData: z.string().catch("fallback"),
processed: z.string().pipe(z.string().transform(s => s.toUpperCase())),
userId: z.string().brand<"UserId">(),
immutable: z.string().readonly(),
});
Custom Validation & Transformation
model CustomValidation {
id String @id @default(cuid())
/// @zod.refine((val) => val.length > 0, { message: "Cannot be empty" })
content String
/// @zod.transform((val) => val.trim().toLowerCase())
slug String
/// @zod.enum(["admin", "user", "guest"])
role String
}
Special Field Types
model SpecialTypes {
id String @id @default(cuid())
/// @zod.json()
metadata Json
/// @zod.custom({ "name": "John", "age": 30, "active": true })
profile Json
}
External Validators with @zod.import
Bring in runtime helpers directly from doc comments when you need logic that lives outside the generated file.
model User {
id String @id @default(cuid())
/// @zod.import(["import { isEmail } from '../validators/email'"])
/// @zod.custom.use(z.string().refine((val) => isEmail(val), { message: 'Invalid email' }))
email String @unique
}
Generated schema (Pure Variant excerpt):
import { isEmail } from '../validators/email';
export const UserSchema = z
.object({
email: z.string().refine((val) => isEmail(val), {
message: 'Invalid email',
}),
// ...
})
.strict();
Import Features
- Provide one or more complete import statements inside the array
- Relative paths are kept intact and rewritten per output directory
- Imports must produce runtime values. Type-only specifiers are detected and omitted
- Field-level imports are merged with model-level imports
- Duplicate statements are emitted once
Model-level imports
Model-level imports can also supply chained refinements:
/// @zod.import(["import { assertCompanyDomain } from '../validators/domain'"]).refine(assertCompanyDomain)
model Organisation {
id String @id @default(cuid())
email String @unique
}
Custom Inline Override (@zod.custom.use)
Replace an entire field schema inline:
model AiChat {
id String @id @default(cuid())
/// @zod.custom.use(z.array(z.object({ role: z.enum(['user','assistant','system']), parts: z.array(z.object({ type: z.enum(['text','image']), text: z.string() })) })))
messages Json @default("[]")
}
Result (excerpt):
messages: z.array(
z.object({
role: z.enum(['user', 'assistant', 'system']),
parts: z.array(z.object({ type: z.enum(['text', 'image']), text: z.string() })),
}),
).default('[]');
This short-circuits other annotations for that field.
Custom Object Schema (@zod.custom)
For JSON fields, use @zod.custom() to define structured object schemas using JavaScript object literals:
model User {
id String @id @default(cuid())
/// @zod.custom({ "title": "User Profile", "description": "User details", "isActive": true })
profile Json
/// @zod.custom({ "settings": { "theme": "dark", "notifications": true }, "preferences": ["email", "sms"] })
metadata Json
}
Result:
// Creates type-safe object schemas
profile: z.union([JsonNullValueInputSchema, z.object({
title: z.string(),
description: z.string(),
isActive: z.boolean()
})]).optional(),
metadata: z.union([JsonNullValueInputSchema, z.object({
settings: z.object({
theme: z.string(),
notifications: z.boolean()
}),
preferences: z.array(z.string())
})]).optional()
Supported Value Types in @zod.custom()
- Strings →
z.string() - Numbers →
z.number().int()orz.number() - Booleans →
z.boolean() - Arrays →
z.array(T)(inferred from first element) - Nested Objects →
z.object({...}) - null →
z.null()
Chaining Support
All methods can be chained together:
model ChainedValidations {
id String @id @default(cuid())
/// @zod.email().max(100).toLowerCase()
email String @unique
/// @zod.nanoid().min(21)
publicId String
/// @zod.min(1).max(50).trim().regex(/^[A-Za-z\s]+$/)
name String
/// @zod.positive().int().multipleOf(5)
score Int
}
Generated schema (Zod v4):
export const ChainedValidationsCreateInputSchema = z.object({
email: z.email().max(100).toLowerCase(),
publicId: z.nanoid().min(21),
name: z.string().min(1).max(50).trim().regex(/^[A-Za-z\s]+$/),
score: z.number().int().positive().multipleOf(5),
});
Native Type Max Length Validation
The generator automatically extracts max length constraints from database native types and applies them as Zod .max() validations.
Supported Native Types
| Database | Native Types | Example | Generated Validation |
|---|---|---|---|
| PostgreSQL | VarChar(n), Char(n) | @db.VarChar(255) | z.string().max(255) |
| MySQL | VarChar(n), Char(n) | @db.VarChar(100) | z.string().max(100) |
| SQL Server | VarChar(n), Char(n), NVarChar(n), NChar(n) | @db.NVarChar(500) | z.string().max(500) |
| SQLite | No length constraints | - | No auto-validation |
| MongoDB | ObjectId | @db.ObjectId | z.string().max(24) |
Basic Usage
model User {
id String @id @default(cuid())
email String @db.VarChar(320) // → z.string().max(320)
displayName String? @db.VarChar(100) // → z.string().max(100).optional()
bio String? @db.Char(500) // → z.string().max(500).optional()
}
Generated schema:
export const UserCreateInputSchema = z.object({
email: z.string().max(320),
displayName: z.string().max(100).optional(),
bio: z.string().max(500).optional(),
});
Conflict Resolution with @zod Comments
When both native types and @zod.max() exist, the more restrictive constraint is used:
model Product {
id String @id @default(cuid())
// Native type wins (more restrictive)
shortName String @db.VarChar(50) /// @zod.max(100)
// @zod wins (more restrictive)
description String? @db.VarChar(1000) /// @zod.max(500)
// Only native constraint
category String @db.VarChar(80)
// Only @zod constraint
tags String? /// @zod.max(200)
}
Generated result:
export const ProductCreateInputSchema = z.object({
shortName: z.string().max(50), // Native type (50) < @zod (100)
description: z.string().max(500).optional(), // @zod (500) < Native (1000)
category: z.string().max(80), // Only native constraint
tags: z.string().max(200).optional(), // Only @zod constraint
});
Complex Validations
Native constraints work alongside other @zod validations:
model Account {
id String @id @default(cuid())
/// @zod.email().toLowerCase()
email String @unique @db.VarChar(320)
/// @zod.min(8).regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
password String @db.VarChar(255)
}
Generated schema:
export const AccountCreateInputSchema = z.object({
email: z.string().max(320).email().toLowerCase(),
password: z
.string()
.max(255)
.min(8)
.regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/),
});
Array Support
Native constraints are applied to array elements:
model Tags {
id String @id @default(cuid())
names String[] @db.VarChar(50) // Each string max 50 chars
}
Generated schema:
export const TagsCreateInputSchema = z.object({
names: z.string().max(50).array(),
});
Version Compatibility
The generator automatically detects your Zod version:
- Zod v4: Uses optimized base types like
z.email(),z.nanoid() - Zod v3: Falls back to chaining methods like
z.string().email()where supported, orz.string()for unsupported methods
Complete Method Reference
String Methods
| Method | Parameters | Description |
|---|---|---|
@zod.min(n) | number, optional error message | Minimum string length |
@zod.max(n) | number, optional error message | Maximum string length |
@zod.length(n) | number, optional error message | Exact string length |
@zod.email() | optional error message/config | Email validation |
@zod.url() | optional error message/config | URL validation |
@zod.uuid() | optional error message/config | UUID validation |
@zod.regex() | pattern, optional error message | Regular expression validation |
@zod.includes() | substring | String must contain substring |
@zod.startsWith() | prefix | String must start with prefix |
@zod.endsWith() | suffix | String must end with suffix |
@zod.trim() | none | Remove leading/trailing whitespace |
@zod.toLowerCase() | none | Convert to lowercase |
@zod.toUpperCase() | none | Convert to uppercase |
@zod.datetime() | optional error message/config | ISO datetime validation |
Zod v4 String Format Methods
| Method | Description | Zod v4 Output | Zod v3 Fallback |
|---|---|---|---|
@zod.httpUrl() | HTTP/HTTPS URL validation | z.httpUrl() | z.string() |
@zod.hostname() | Hostname validation | z.hostname() | z.string() |
@zod.nanoid() | Nanoid validation | z.nanoid() | z.string() |
@zod.cuid() | CUID validation | z.cuid() | z.string() |
@zod.cuid2() | CUID v2 validation | z.cuid2() | z.string() |
@zod.ulid() | ULID validation | z.ulid() | z.string() |
@zod.base64() | Base64 validation | z.base64() | z.string() |
@zod.base64url() | Base64URL validation | z.base64url() | z.string() |
@zod.hex() | Hexadecimal validation | z.hex() | z.string() |
@zod.jwt() | JWT token validation | z.jwt() | z.string() |
@zod.hash(algo) | Hash validation | z.hash("sha256") | z.string() |
@zod.ipv4() | IPv4 address validation | z.ipv4() | z.string() |
@zod.ipv6() | IPv6 address validation | z.ipv6() | z.string() |
@zod.cidrv4() | CIDR v4 validation | z.cidrv4() | z.string() |
@zod.cidrv6() | CIDR v6 validation | z.cidrv6() | z.string() |
@zod.emoji() | Single emoji validation | z.emoji() | z.string() |
@zod.isoDate() | ISO date validation | z.iso.date() | z.string() |
@zod.isoTime() | ISO time validation | z.iso.time() | z.string() |
@zod.isoDatetime() | ISO datetime validation | z.iso.datetime() | z.string() |
@zod.isoDuration() | ISO duration validation | z.iso.duration() | z.string() |
Number Methods
| Method | Parameters | Field Types | Description |
|---|---|---|---|
@zod.min(n) | number, optional error message | Int, Float, BigInt | Minimum value |
@zod.max(n) | number, optional error message | Int, Float, BigInt | Maximum value |
@zod.int() | none | Int, Float | Integer validation |
@zod.positive() | optional error message | Int, Float, BigInt | Positive number (> 0) |
@zod.negative() | none | Int, Float, BigInt | Negative number (< 0) |
@zod.nonnegative() | none | Int, Float, BigInt | Non-negative number (≥ 0) |
@zod.nonpositive() | none | Int, Float, BigInt | Non-positive number (≤ 0) |
@zod.finite() | none | Float | Finite number |
@zod.safe() | none | Int, Float | Safe integer |
@zod.multipleOf(n) | number, optional error message | Int, Float | Multiple of validation |
Array Methods
| Method | Parameters | Description |
|---|---|---|
@zod.min(n) | number | Minimum array length |
@zod.max(n) | number | Maximum array length |
@zod.length(n) | number | Exact array length |
@zod.nonempty() | none | Non-empty array |
Date Methods
| Method | Parameters | Description |
|---|---|---|
@zod.min(date) | Date | Minimum date |
@zod.max(date) | Date | Maximum date |
Field Modifiers
| Method | Parameters | Description |
|---|---|---|
@zod.optional() | none | Make field optional |
@zod.nullable() | none | Make field nullable |
@zod.nullish() | none | Make field nullish (null or undefined) |
@zod.default(value) | any value | Set default value |
Custom Validation
| Method | Parameters | Description |
|---|---|---|
@zod.refine(fn) | function | Custom validation function |
@zod.transform(fn) | function | Value transformation |
@zod.enum(options) | array | Enum validation |
Special Types
| Method | Parameters | Field Types | Description |
|---|---|---|---|
@zod.json() | none | Json | JSON validation |
@zod.object() | none | Any | Object validation |
@zod.array() | optional schema | Any | Array validation |
@zod.custom(schema) | object literal | Json | Structured object schema |
Parameter Types Supported
The generator preserves all JavaScript parameter types:
- Strings:
@zod.nanoid('Custom error')→z.nanoid('Custom error') - Objects:
@zod.nanoid({ abort: true })→z.nanoid({"abort":true}) - Numbers:
@zod.min(10)→z.string().min(10) - Booleans:
@zod.optional()→z.string().optional() - Arrays:
@zod.custom([1, 2, 3])→z.custom([1,2,3]) - RegExp:
@zod.regex(/pattern/)→z.regex(/pattern/) - Function calls:
@zod.custom(Date.now())→z.custom(Date.now()) - Nested expressions:
@zod.custom(new RegExp('.'))→z.custom(new RegExp('.'))