Zod Schemas
Object Schemas
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1),
age: z.number().min(0),
email: z.string().email(),
role: z.enum(['admin', 'user', 'guest']),
});
type User = z.infer<typeof UserSchema>;
Nested Objects
const AddressSchema = z.object({
street: z.string(),
city: z.string(),
country: z.string(),
});
const PersonSchema = z.object({
name: z.string(),
address: AddressSchema,
});
Union Types
// Either string or number
z.union([z.string(), z.number()]);
// Shorthand
z.string().or(z.number());
// Discriminated union
const ResponseSchema = z.discriminatedUnion('status', [
z.object({ status: z.literal('success'), data: z.any() }),
z.object({ status: z.literal('error'), error: z.string() }),
]);
const StringToNumber = z.string().transform((val) => parseInt(val, 10));
const TrimmedString = z.string().transform((val) => val.trim());
// Chain transforms
const Schema = z.string()
.transform((val) => val.toLowerCase())
.transform((val) => val.trim());
Refinements (Custom Validation)
const PasswordSchema = z.string()
.min(8)
.refine((val) => /[A-Z]/.test(val), {
message: "Must contain uppercase letter",
})
.refine((val) => /[0-9]/.test(val), {
message: "Must contain number",
});
Partial & Pick
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string(),
});
// All fields optional
const PartialUser = UserSchema.partial();
// Pick specific fields
const UserName = UserSchema.pick({ name: true });
// Omit fields
const UserWithoutId = UserSchema.omit({ id: true });
Extend
const BasicUser = z.object({
name: z.string(),
});
const AdminUser = BasicUser.extend({
role: z.literal('admin'),
permissions: z.array(z.string()),
});