Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript

Zod no TypeScript: Validação de Dados

Atualizado em: 23 de julho de 2026

Desenvolvedor validando dados com Zod no TypeScript

Aplicações TypeScript trabalham o tempo todo com dados que chegam de fora: respostas de APIs, formulários, arquivos JSON, variáveis de ambiente e mensagens recebidas por filas. Embora o TypeScript ajude a detectar erros durante o desenvolvimento, ele não valida automaticamente o conteúdo que chega em tempo de execução. É justamente nesse ponto que o Zod no TypeScript se torna útil.

Neste guia, você vai aprender a instalar o Zod, criar schemas, validar objetos, tratar erros, transformar valores e integrar a biblioteca a uma API Node.js. O objetivo é construir uma base prática que possa ser reutilizada em back-end, front-end e projetos full stack.

O que é Zod?

Zod é uma biblioteca de validação orientada a schemas e projetada para funcionar muito bem com TypeScript. Você descreve o formato esperado de um dado e usa esse schema para validar valores reais durante a execução. A partir do mesmo schema, o TypeScript também consegue inferir o tipo correspondente.

Isso resolve um problema comum: uma interface TypeScript desaparece quando o código é compilado para JavaScript. Portanto, declarar uma interface não impede que uma API envie campos ausentes, números em formato de texto ou valores inesperados. O Zod faz essa verificação quando o programa está rodando.

Para revisar os fundamentos da linguagem, consulte o artigo o que é TypeScript. Também vale conhecer como o JavaScript funciona, pois a validação acontece no código JavaScript gerado.

Instalando o Zod

Crie um projeto Node.js com TypeScript ou use um projeto existente. Depois, instale a biblioteca:

npm install zod

Em seguida, importe o objeto z:

import { z } from "zod";

Se você ainda não configurou o ambiente, o artigo o que é Node.js apresenta os conceitos principais da plataforma.

Criando o primeiro schema

Imagine que uma aplicação recebe dados para cadastrar um usuário. O nome deve ser uma string, o e-mail precisa ser válido e a idade deve ser um número inteiro positivo.

const UserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().int().positive()
});

O schema descreve exatamente o formato aceito. Agora podemos validar um objeto com o método parse:

const user = UserSchema.parse({
  name: "Marina",
  email: "marina@example.com",
  age: 28
});

console.log(user);

Quando os dados são válidos, parse devolve o valor já tipado. Quando são inválidos, ele lança um erro de validação. Esse comportamento é útil quando a falha deve interromper o fluxo imediatamente.

parse ou safeParse?

Nem sempre queremos usar try/catch. O método safeParse retorna um objeto indicando se a validação funcionou. Isso facilita o tratamento de formulários e requisições HTTP.

const result = UserSchema.safeParse({
  name: "A",
  email: "email-invalido",
  age: -2
});

if (!result.success) {
  console.log(result.error.issues);
} else {
  console.log(result.data);
}

No bloco de sucesso, result.data possui o tipo inferido pelo schema. No bloco de falha, result.error.issues contém informações como caminho do campo, mensagem e código do erro.

Inferindo tipos automaticamente

Uma das principais vantagens do Zod é evitar a duplicação entre schema e tipo. Podemos criar um tipo diretamente a partir do schema:

type User = z.infer<typeof UserSchema>;

function saveUser(user: User) {
  console.log(`Salvando ${user.email}`);
}

Assim, o schema se torna a fonte de verdade. Se você adicionar ou remover um campo, o tipo é atualizado automaticamente. Essa prática reduz inconsistências em projetos maiores.

Campos opcionais, nulos e valores padrão

O Zod permite modelar situações comuns de forma explícita:

const ProfileSchema = z.object({
  nickname: z.string().optional(),
  bio: z.string().nullable(),
  active: z.boolean().default(true)
});
  • optional() aceita o campo ausente ou com valor undefined.
  • nullable() permite o valor null.
  • default() fornece um valor quando o campo não é enviado.

Não trate optional e nullable como sinônimos. Uma API pode distinguir entre “campo não enviado” e “campo enviado sem valor”.

Validando arrays e objetos aninhados

Schemas podem ser combinados. Um pedido, por exemplo, pode conter cliente, itens e endereço:

const ProductSchema = z.object({
  id: z.string().uuid(),
  name: z.string().min(1),
  price: z.number().nonnegative()
});

const OrderSchema = z.object({
  customerEmail: z.string().email(),
  items: z.array(ProductSchema).min(1),
  address: z.object({
    city: z.string(),
    state: z.string().length(2)
  })
});

A composição ajuda a reutilizar regras. Se o produto aparece em vários endpoints, o mesmo ProductSchema pode ser usado em todos eles.

Transformando dados durante a validação

Dados de formulários normalmente chegam como texto. O Zod pode transformar valores depois de validá-los:

const SearchSchema = z.object({
  query: z.string().trim().min(1),
  page: z.string().transform((value) => Number(value))
});

const search = SearchSchema.parse({
  query: "  typescript  ",
  page: "2"
});

console.log(search.query); // typescript
console.log(search.page);  // 2

Apesar de ser possível transformar diretamente, valide conversões com cuidado. Number("abc") produz NaN. Em campos críticos, prefira coerção acompanhada de regras como número inteiro e limite mínimo.

Refinamentos personalizados

Algumas regras dependem de mais de um campo. Um formulário de senha pode exigir que a confirmação seja igual à senha principal:

const PasswordSchema = z.object({
  password: z.string().min(8),
  confirmPassword: z.string()
}).refine(
  (data) => data.password === data.confirmPassword,
  {
    message: "As senhas não coincidem",
    path: ["confirmPassword"]
  }
);

O campo path associa a mensagem ao local correto. Isso ajuda a interface a mostrar o erro abaixo do campo de confirmação.

Usando Zod em uma API Node.js

Em uma API, valide os dados antes de acessar banco de dados ou executar regras de negócio. Veja um exemplo simplificado com Express:

import express from "express";
import { z } from "zod";

const app = express();
app.use(express.json());

const CreateUserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().int().min(18)
});

app.post("/users", (req, res) => {
  const result = CreateUserSchema.safeParse(req.body);

  if (!result.success) {
    return res.status(400).json({
      message: "Dados inválidos",
      errors: result.error.issues
    });
  }

  const user = result.data;

  return res.status(201).json({
    id: crypto.randomUUID(),
    ...user
  });
});

app.listen(3000);

O objeto req.body deve ser tratado como dado não confiável. Depois do safeParse, o restante da rota trabalha com um objeto validado e tipado. Para entender melhor esse fluxo, veja o que é API REST e o tutorial como criar uma API com Node.js.

Mensagens de erro para o usuário

Não envie detalhes internos desnecessários. Converta os erros para uma estrutura previsível. Um formato simples pode usar o caminho do campo e a mensagem:

const formattedErrors = result.error.issues.map((issue) => ({
  field: issue.path.join("."),
  message: issue.message
}));

Também evite depender apenas das mensagens para implementar lógica. O front-end deve usar campos estáveis sempre que possível, enquanto o texto pode ser traduzido ou alterado.

Validação não substitui segurança

Zod confirma formato e regras declaradas, mas não substitui autenticação, autorização, consultas parametrizadas, limite de requisições e sanitização adequada. Um e-mail válido não prova que o usuário controla aquele endereço. Uma string dentro do limite ainda pode conter conteúdo inadequado para determinado contexto.

Use validação como uma camada de defesa. Para uma visão mais ampla, consulte o guia sobre segurança em aplicações web.

Testando os schemas

Schemas são regras importantes do sistema e merecem testes. Crie casos para valores válidos, campos ausentes, formatos incorretos e limites. Um exemplo com Jest:

test("rejeita usuário menor de idade", () => {
  const result = CreateUserSchema.safeParse({
    name: "João",
    email: "joao@example.com",
    age: 16
  });

  expect(result.success).toBe(false);
});

O artigo sobre testes unitários com Jest mostra como organizar a suíte de testes.

Boas práticas com Zod

  • Valide nas fronteiras: faça a validação quando os dados entram na aplicação.
  • Centralize schemas reutilizáveis: evite copiar regras entre rotas.
  • Use nomes claros: nomes como CreateUserSchema indicam o propósito.
  • Não aceite campos sem necessidade: defina apenas o que o endpoint realmente usa.
  • Teste casos inválidos: a maior parte dos bugs aparece nas bordas.
  • Separe validação de negócio: formato de e-mail é validação; verificar se o e-mail já existe é regra de negócio.
  • Retorne erros consistentes: mantenha o mesmo formato em todos os endpoints.

Documentação oficial

A documentação oficial do Zod apresenta os tipos, métodos de composição, transformações e tratamento de erros. Para compreender como o TypeScript reduz tipos após verificações, consulte também a documentação oficial sobre narrowing.

Conclusão

Usar Zod no TypeScript permite transformar dados externos desconhecidos em valores confiáveis para o restante da aplicação. Você cria um schema, valida em tempo de execução e ainda obtém tipos inferidos automaticamente.

Comece validando uma única rota ou formulário. Depois, extraia schemas reutilizáveis, padronize os erros e adicione testes. Essa estrutura reduz falhas silenciosas e torna o código mais previsível, principalmente quando a aplicação integra APIs, bancos de dados e interfaces diferentes.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita