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.body corresponda 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

Construído com Eleventy · busca por Lunr.js