zod
Zod valida datos en los límites de la API y la configuración para que la entrada inválida nunca llegue a la lógica de negocio. Un esquema proporciona comprobaciones en tiempo de ejecución y tipos de TypeScript sin desviaciones.
Busca en todas las páginas de la documentación
Zod valida datos en los límites de la API y la configuración para que la entrada inválida nunca llegue a la lógica de negocio. Un esquema proporciona comprobaciones en tiempo de ejecución y tipos de TypeScript sin desviaciones.
Tarjeta de receta de referencia rápida: lista para copiar y pegar.
import { z } from "zod";
export const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(120),
role: z.enum(["member", "admin"]).default("member"),
});
export type CreateUserInput = z.infer<typeof createUserSchema>;
// Ruta de Fastify
app.post("/users", async (req, reply) => {
const parsed = createUserSchema.safeParse(req.body);
if (!parsed.success) {
return reply.status(400).send({ errors: parsed.error.flatten() });
}
return createUser(parsed.data);
});Cuándo usarlo:
process.env al inicio (consulta también Validación de Zod y env-schema)// src/schemas/order.ts
import { z } from "zod";
export const orderItemSchema = z.object({
sku: z.string().uuid(),
quantity: z.coerce.number().int().positive(),
});
export const createOrderSchema = z.object({
customerId: z.string().uuid(),
items: z.array(orderItemSchema).min(1),
shipBy: z.string().datetime().optional(),
});
export type CreateOrderInput = z.infer<typeof createOrderSchema>;
// src/routes/orders.ts
import type { FastifyInstance } from "fastify";
import { createOrderSchema } from "../schemas/order.js";
export async function orderRoutes(app: FastifyInstance): Promise<void> {
app.post("/orders", async (req, reply) => {
const result = createOrderSchema.safeParse(req.body);
if (!result.success) {
req.log.warn({ validation: result.error.flatten() }, "invalid_order");
return reply.status(400).send({
error: "validation_failed",
details: result.error.flatten().fieldErrors,
});
}
const order = await createOrder(result.data);
return reply.status(201).send(order);
});
}// src/workers/email.worker.ts
import { z } from "zod";
const emailJobSchema = z.object({
to: z.string().email(),
templateId: z.string(),
vars: z.record(z.string()),
});
export async function processEmailJob(raw: unknown): Promise<void> {
const job = emailJobSchema.parse(raw); // lanza → reintento/DLQ de BullMQ
await sendEmail(job);
}Comportamientos clave:
safeParse para HTTP: mapea a 400 sin lanzar excepcionesparse para workers: permite que la cola reintente los mensajes venenososz.coerce.number() para cadenas de consulta y variables de entorno.refine() para reglas entre campos (por ejemplo, shipBy debe ser futuro)| Enfoque | Pros | Contras |
|---|---|---|
| Zod | Inferencia de TS, componible, gran DX | Costo en tiempo de ejecución en rutas críticas (generalmente insignificante) |
| Solo JSON Schema | Nativo de Fastify, exportación OpenAPI | No hay tipos TS compartidos sin generación de código |
Comprobaciones if manuales | Cero dependencias | Se desvía de los tipos; patrones no testeables |
| class-validator (Nest) | Ecosistema de Nest | Magia de decoradores; menos portable |
@fastify/type-provider-typebox o convierten Zod a JSON Schema para la serialización de respuestas.nestjs-zod o pipes globales con DTOs de Zod.import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { createOrderSchema } from "./order.js";
describe("createOrderSchema", () => {
it("rechaza elementos vacíos", () => {
const result = createOrderSchema.safeParse({
customerId: "550e8400-e29b-41d4-a716-446655440000",
items: [],
});
assert.equal(result.success, false);
});
});__fixtures__/invalid-orders.json.Valida en los límites (HTTP, entorno, cola, webhooks externos). Dentro de la lógica de dominio, confía en los valores tipados. No vuelvas a analizar el mismo objeto tres veces por solicitud.
Mapea error.flatten().fieldErrors a tu estándar de errores de API. Nunca devuelvas el stack de ZodError o issues en bruto en producción.
Usa Zod como fuente de verdad y genera OpenAPI con zod-to-openapi o mantén un JSON Schema paralelo para la documentación pública. Elige una fuente, no ambas editadas a mano.
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS activa), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.
Revisado por Chris St. John·Última actualización: 19 jul 2026