Acesse o painel da sua conta

Não tem uma conta? Registrar

Entrar em contato

Visite também nosso site craftxp.com.br

  • img
  • img
  • img
  • img
  • img
  • img

Entre em contato

Zod na Prática: Validação de Dados Type-First com TypeScript do Zero ao Avançado

Zod na Prática: Validação de Dados Type-First com TypeScript do Zero ao Avançado

O que é Zod?

Zod é uma biblioteca de validação de dados type-first para TypeScript que permite definir esquemas de validação de forma declarativa e inferir tipos automaticamente. Criada por Colin McDonnell, a Zod se tornou o padrão da indústria para validação de dados no ecossistema TypeScript, com mais de 30 milhões de downloads semanais no npm.

Diferente de bibliotecas como Joi ou Yup, a Zod foi projetada desde o início com TypeScript em mente, oferecendo inferência de tipos impecável e uma API minimalista que elimina a necessidade de manter tipos e validadores em sincronia manualmente.

Por que Zod?

Em aplicações modernas, os dados chegam de diversas fontes — APIs externas, formulários, arquivos de configuração, bancos de dados. Cada uma dessas fontes pode enviar dados inesperados ou malformados. Sem validação robusta, você corre o risco de propagar dados inválidos pelo sistema, causando bugs difíceis de rastrear.

A Zod resolve este problema com:

  • Inferência automática de tipos: O tipo TypeScript é derivado do esquema, não o contrário
  • API encadeável: Esquemas compostos de forma intuitiva
  • Mensagens de erro customizáveis: Controle total sobre as mensagens retornadas
  • Zero dependências: Leve e rápido
  • Suporte a parsing assíncrono: Validações que dependem de dados externos

Instalação

Adicionar Zod ao seu projeto é simples com npm ou yarn:

npm install zod
# ou
yarn add zod
# ou
pnpm add zod

Não é necessário configurar nada — a Zod funciona out-of-the-box com TypeScript 4.1+.

Primeiros Passos: Esquemas Básicos

Vamos começar com exemplos práticos de esquemas fundamentais:

import { z } from "zod";

// String
const nomeSchema = z.string();
type Nome = z.infer<typeof nomeSchema>; // string

// Número com validações
const idadeSchema = z.number().min(0).max(150);
type Idade = z.infer<typeof idadeSchema>; // number

// Booleano
const ativoSchema = z.boolean();

// Validação de email
const emailSchema = z.string().email("Email inválido");

// Validação de URL
const urlSchema = z.string().url();

// Enum
const StatusSchema = z.enum(["ativo", "inativo", "pendente"]);
type Status = z.infer<typeof StatusSchema>;
// "ativo" | "inativo" | "pendente"

Objetos e Composição de Esquemas

O poder da Zod aparece na validação de objetos complexos. Vamos modelar um usuário:

const UsuarioSchema = z.object({
  id: z.string().uuid(),
  nome: z.string().min(3, "Nome deve ter no mínimo 3 caracteres").max(100),
  email: z.string().email("Formato de email inválido"),
  idade: z.number().int().positive().optional(),
  endereco: z.object({
    rua: z.string().min(1),
    cidade: z.string().min(1),
    cep: z.string().regex(/^\d{5}-\d{3}$/, "CEP deve seguir o formato 00000-000"),
  }).optional(),
  roles: z.array(z.enum(["admin", "user", "moderator"])).min(1),
});

type Usuario = z.infer<typeof UsuarioSchema>;
// O tipo Usuario é inferido AUTOMATICAMENTE do esquema!

Parsing e Tratamento de Erros

A Zod oferece três estratégias para validar dados:

parse() — Modo Eager

Lança uma exceção detalhada se os dados forem inválidos:

try {
  const usuario = UsuarioSchema.parse(dadosRecebidos);
  // usuario possui tipo Usuario — totalmente tipado
  console.log(usuario.nome);
} catch (erro) {
  if (erro instanceof z.ZodError) {
    console.error("Erros de validação:", erro.errors);
    // erro.errors é um array de { path, message, code }
  }
}

safeParse() — Modo Seguro (Recomendado)

Retorna um objeto com sucesso/erro, sem lançar exceções:

const resultado = UsuarioSchema.safeParse(dadosRecebidos);

if (resultado.success) {
  // resultado.data é Usuario
  console.log("Dados válidos:", resultado.data);
} else {
  // resultado.error é ZodError
  console.log("Erros:", resultado.error.format());
  /* Formato:
  {
    _errors: [],
    nome: { _errors: ["Nome deve ter no mínimo 3 caracteres"] },
    email: { _errors: ["Formato de email inválido"] }
  }
  */
}

Transformação e Refinamento

A Zod permite transformar dados durante a validação, criando pipelines poderosos:

const EmailLimpoSchema = z.string()
  .email()
  .transform(email => email.toLowerCase().trim());

const SlugSchema = z.string()
  .transform(slug => slug
    .toLowerCase()
    .replace(/\s+/g, '-')
    .replace(/[^a-z0-9-]/g, '')
  );

// Refinamento personalizado
const SenhaForteSchema = z.string()
  .min(8, "Senha deve ter no mínimo 8 caracteres")
  .refine(senha => /[A-Z]/.test(senha), {
    message: "Senha deve conter pelo menos uma letra maiúscula",
  })
  .refine(senha => /[0-9]/.test(senha), {
    message: "Senha deve conter pelo menos um número",
  });

SuperRefine: Validação Avançada

Para validações complexas que dependem de múltiplos campos, use superRefine:

const CadastroSchema = z.object({
  senha: z.string().min(8),
  confirmacaoSenha: z.string(),
  email: z.string().email(),
}).superRefine((dados, ctx) => {
  if (dados.senha !== dados.confirmacaoSenha) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: "Senhas não conferem",
      path: ["confirmacaoSenha"],
    });
  }
});

Integração com React Hook Forms

Uma das aplicações mais comuns da Zod é com formulários React. A integração com React Hook Form é nativa:

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const FormularioSchema = z.object({
  nome: z.string().min(3, "Nome muito curto"),
  email: z.string().email("Email inválido"),
  idade: z.coerce.number().min(18, "Idade mínima: 18 anos"),
});

type FormData = z.infer<typeof FormularioSchema>;

function MeuFormulario() {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
    resolver: zodResolver(FormularioSchema),
  });

  const onSubmit = (data: FormData) => {
    console.log("Dados válidos:", data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("nome")} placeholder="Nome" />
      {errors.nome && <span>{errors.nome.message}</span>}

      <input {...register("email")} placeholder="Email" />
      {errors.email && <span>{errors.email.message}</span>}

      <input type="number" {...register("idade")} placeholder="Idade" />
      {errors.idade && <span>{errors.idade.message}</span>}

      <button type="submit">Enviar</button>
    </form>
  );
}

Validação de APIs com Zod

Em APIs REST ou GraphQL, a Zod é perfeita para validar dados de entrada no servidor:

// Exemplo com Next.js API Route
import { z } from "zod";
import { NextResponse } from "next/server";

const CriarUsuarioSchema = z.object({
  nome: z.string().min(3).max(100),
  email: z.string().email(),
  tipo: z.enum(["free", "pro", "enterprise"]).default("free"),
});

export async function POST(request: Request) {
  try {
    const body = await request.json();
    const dados = CriarUsuarioSchema.parse(body);

    // dados está totalmente tipado como:
    // { nome: string; email: string; tipo: "free" | "pro" | "enterprise" }
    const usuario = await criarUsuario(dados);
    return NextResponse.json(usuario, { status: 201 });
  } catch (erro) {
    if (erro instanceof z.ZodError) {
      return NextResponse.json(
        { erro: "Dados inválidos", detalhes: erro.errors },
        { status: 400 }
      );
    }
    return NextResponse.json(
      { erro: "Erro interno" },
      { status: 500 }
    );
  }
}

Zod + TypeScript: A Magia da Inferência

O grande diferencial da Zod é a inferência de tipos. Veja como ela elimina duplicação:

// ❌ ABORDAGEM TRADICIONAL: tipos e validadores separados
type Produto = {
  id: string;
  nome: string;
  preco: number;
  categorias: string[];
};

function validarProduto(dados: unknown): dados is Produto {
  return (
    typeof dados === "object" &&
    dados !== null &&
    typeof dados.id === "string" &&
    // ... dezenas de linhas de validação manual
  );
}

// ✅ COM ZOD: um esquema gera tipo + validador
const ProdutoSchema = z.object({
  id: z.string().uuid(),
  nome: z.string().min(1).max(200),
  preco: z.number().positive(),
  categorias: z.array(z.string()).min(1),
});

type Produto = z.infer<typeof ProdutoSchema>;
// ✅ ProdutoSchema.parse() valida em tempo de execução
// ✅ type Produto é inferido em tempo de compilação
// ✅ Um único schema mantém tudo sincronizado

Casos de Uso Avançados

Discriminated Unions

Para dados que podem ter diferentes formatos dependendo de um campo discriminador:

const EventoSchema = z.discriminatedUnion("tipo", [
  z.object({
    tipo: z.literal("click"),
    elemento: z.string(),
    coordenadas: z.tuple([z.number(), z.number()]),
  }),
  z.object({
    tipo: z.literal("submit"),
    formulario: z.string(),
    dados: z.record(z.string()),
  }),
  z.object({
    tipo: z.literal("navegacao"),
    destino: z.string().url(),
  }),
]);

type Evento = z.infer<typeof EventoSchema>;
// Evento é: { tipo: "click"; elemento: string; coordenadas: [number, number] }
//          | { tipo: "submit"; formulario: string; dados: Record<string, string> }
//          | { tipo: "navegacao"; destino: string }

z.coerce — Coerção Automática

Converta strings para números automaticamente em formulários:

const InputSchema = z.object({
  idade: z.coerce.number().int().min(0).max(150),
  preco: z.coerce.number().positive(),
  ativo: z.coerce.boolean(),
});

// InputSchema.parse({ idade: "25", preco: "99.90", ativo: "true" })
// → { idade: 25, preco: 99.9, ativo: true }

Conclusão

A Zod se consolidou como a biblioteca de validação de dados mais importante do ecossistema TypeScript. Sua combinação de API intuitiva, inferência de tipos impecável e zero dependências a torna a escolha ideal para qualquer projeto — desde pequenos scripts até aplicações enterprise.

Se você ainda não usa Zod nos seus projetos, comece hoje mesmo. A instalação leva segundos, a integração é imediata, e os benefícios — código mais seguro, tipos mais precisos, e eliminação de bugs causados por dados malformados — são sentidos desde o primeiro uso.

Com a crescente adoção de TypeScript no mercado, dominar ferramentas como Zod não é mais diferencial — é requisito para desenvolvimento profissional de software moderno.

Craft XP
Craft XP