| name | zod-guidelines |
| description | Data validation guidelines using Zod including schemas, API request validation, form validation, and error handling. Auto-loaded when working with validation code. |
| category | guideline |
| user-invocable | false |
Data Validation Guidelines
Core Principles
- Validate at boundaries - API endpoints, form submissions, external data
- Fail fast - Reject invalid data immediately
- Be specific - Clear error messages for each validation failure
- Type safety - Schema validation provides runtime types
- Defense in depth - Client and server validation
Schema Validation with Zod
Basic Schemas
import { z } from 'zod';
const stringSchema = z.string();
const numberSchema = z.number();
const booleanSchema = z.boolean();
const dateSchema = z.date();
const emailSchema = z.string().email();
const positiveNumber = z.number().positive();
const nonEmptyString = z.string().min(1);
const userSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150).optional(),
role: z.enum(['admin', 'user', 'guest']),
createdAt: z.date(),
});
type User = z.infer<typeof userSchema>;
Common Patterns
const configSchema = z.object({
port: z.number().default(3000),
debug: z.boolean().default(false),
});
const schema = z.object({
optional: z.string().optional(),
nullable: z.string().nullable(),
both: z.string().nullish(),
});
const trimmedString = z.string().trim();
const lowercaseEmail = z.string().email().toLowerCase();
const parsedDate = z.string().transform(s => new Date(s));
const coercedNumber = z.coerce.number();
const coercedBoolean = z.coerce.boolean();
coercedDate = z..();
Validation and Parsing
try {
const user = userSchema.parse(input);
} catch (error) {
if (error instanceof z.ZodError) {
console.error(error.errors);
}
}
const result = userSchema.safeParse(input);
if (result.success) {
const user = result.data;
} else {
const errors = result.error.errors;
}
const partialUser = userSchema.partial();
const nameOnly = userSchema.pick({ name: true, email: true });
const withoutId = userSchema.omit({ id: true });
API Request Validation
Express Middleware
import { z } from 'zod';
import { Request, Response, NextFunction } from 'express';
function validate<T extends z.ZodSchema>(schema: T) {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse({
body: req.body,
query: req.query,
params: req.params,
});
if (!result.success) {
return res.status(400).json({
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid request data',
details: result.error.errors.map(e => ({
path: e.path.join('.'),
message: e.,
})),
},
});
}
req. = result.;
();
};
}
createUserSchema = z.({
: z.({
: z.().(),
: z.().(),
}),
});
app.(, (createUserSchema), {
{ name, email } = req..;
});
Request Schema Patterns
const listUsersSchema = z.object({
query: z.object({
page: z.coerce.number().min(1).default(1),
limit: z.coerce.number().min(1).max(100).default(20),
search: z.string().optional(),
status: z.enum(['active', 'inactive']).optional(),
}),
});
const getUserSchema = z.object({
params: z.object({
id: z.string().uuid(),
}),
});
const updateUserSchema = z.object({
params: z.object({
id: z.string().uuid(),
}),
body: z.object({
name: z.string().min(1).().(),
: z.().().(),
}).(
.(data). > ,
{ : }
),
});
Custom Validators
Reusable Validators
const stringId = () => z.string().uuid();
const pagination = () => z.object({
page: z.coerce.number().min(1).default(1),
limit: z.coerce.number().min(1).max(100).default(20),
});
const dateString = () => z.string().refine(
s => !isNaN(Date.parse(s)),
{ message: 'Invalid date format' }
).transform(s => new Date(s));
const listSchema = z.object({
query: pagination(),
});
Known Gotchas
Coercion Behavior
z.coerce.number().parse('');
z.coerce.number().parse('abc');
z.coerce.number().parse(null);
const safeNumber = z.coerce.number().refine(
n => !Number.isNaN(n),
{ message: 'Invalid number' }
);
Optional vs Nullable
z.string().optional()
z.string().nullable()
z.string().nullish()
z.string().optional().default('hello')
z.string().nullable().default('hello')
Parse vs SafeParse
const data = schema.parse(trustedInput);
const result = schema.safeParse(userInput);
if (!result.success) {
}
Additional References
- Form Validation - Client-side form validation with React Hook Form and field-level validation
- Advanced Patterns - Refinements, error handling, sanitization, and complex validation patterns