Validação com Zod em Middlewares
Express & Next.js
O Zod é uma biblioteca de declaração e validação de schemas focada na adoção perfeita do TypeScript. Uma das melhores práticas no ecossistema Node.js (seja Express, Next.js, etc.) é usar o Zod num middleware para validar a requisição antes que ela sequer chegue ao Controlador principal.
Por que usar em um middleware?
- Fail-Fast (Falha Rápida): Requisições com formato inválido são rejeitadas imediatamente (com um erro
400 Bad Request), poupando ciclos de CPU e evitando acessos desnecessários ao banco de dados. - Tipagem Inferida: Ao passar pelo middleware, o Zod garante em tempo de compilação que o objeto
req.bodycorresponda exatamente ao tipo esperado. - Código Limpo: Remove aquela série de verificações manuais (
if (!req.body.name) return res...) de dentro das suas lógicas de negócio.
Exemplo: Middleware Genérico no Express
Crie uma “fábrica” de middleware que recebe um schema do Zod genérico e executa a validação:
import { Request, Response, NextFunction } from "express";
import { AnyZodObject, ZodError } from "zod";
export const validate =
(schema: AnyZodObject) => (req: Request, res: Response, next: NextFunction) => {
try {
// Faz o parse do body, query ou params e lança erro se for inválido
schema.parse({
body: req.body,
query: req.query,
params: req.params,
});
next(); // Passou! Vai para o próximo handler.
} catch (error) {
if (error instanceof ZodError) {
return res.status(400).json({
message: "Erro de Validação",
errors: error.errors,
});
}
next(error); // Erro genérico é repassado
}
};
Como plugar isso em uma rota:
import { z } from "zod";
// Define o schema esperado
const userSchema = z.object({
body: z.object({
name: z.string().min(2),
email: z.string().email(),
}),
});
// A rota só é executada se o validate(userSchema) passar
app.post("/users", validate(userSchema), (req, res) => {
// A partir daqui, req.body já está validado!
res.status(201).send("Usuário criado");
});
Exemplo: Validação no Next.js (App Router / Route Handlers)
Embora o Next.js não tenha a mesma corrente de “middlewares de rota” do Express, é possível encapsular a lógica nativamente nos Route Handlers:
import { NextResponse } from "next/server";
import { z } from "zod";
const bodySchema = z.object({
id: z.string(),
});
export async function POST(req: Request) {
try {
const body = await req.json();
// Faz o parse manual. Se falhar, vai pro catch
const data = bodySchema.parse(body);
return NextResponse.json({ success: true, data });
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({ errors: error.errors }, { status: 400 });
}
return NextResponse.json({ error: "Erro interno do servidor" }, { status: 500 });
}
}
Relacionadas: express-middleware-chain · express-fluxo-de-trabalho