4.6 KiB
4.6 KiB
| name | scope | description |
|---|---|---|
| criar-schema-zod | global | Guia operacional para criar, organizar e manter schemas de validação Zod em projetos TypeScript. |
Skill: Criar Schema Zod
Use esta skill ao adicionar validação de dados em qualquer ponto de entrada do sistema — API, formulário, webhook, importação, fila ou variáveis de ambiente.
PASSO 1 — Identificar a Entrada Externa
Determinar qual dado externo precisa de validação:
| Tipo de entrada | Onde validar |
|---|---|
| Corpo de requisição (POST/PUT) | Handler da rota, antes de qualquer lógica |
| Query/path params | Handler da rota |
| Formulário frontend | Antes de chamar a API |
| Dados de fila (BullMQ) | No processador da fila |
| Arquivo importado (CSV/JSON) | No parser, linha a linha |
| Variáveis de ambiente | No boot da aplicação |
| Resposta de API externa | Após receber a resposta |
PASSO 2 — Localizar ou Criar o Schema no Pacote Comuns
Schemas compartilhados vivem no pacote *-comuns:
projeto-comuns/src/tabelas/
├── usuarios/
│ ├── zUsuarioCriar.ts
│ ├── zUsuarioEditar.ts
│ └── index.ts
└── index.ts
2.1. Verificar se já existe
rg "z{NomeDaEntidade}" --include='*.ts' -l
Se já existir → reutilizar ou compor a partir dele (.extend(), .pick(), .partial()).
2.2. Criar o schema
import { z } from "zod"
export const zUsuarioCriar = z.object({
nome: z.string().trim().min(1, "Nome é obrigatório"),
email: z.string().email("E-mail inválido"),
ativo: z.boolean().default(true),
})
2.3. Nomenclatura obrigatória
| Padrão | Uso |
|---|---|
z{Entidade}Criar |
Criação |
z{Entidade}Editar |
Edição (geralmente .partial()) |
z{Entidade}Filtro |
Filtros de listagem |
z{Entidade}Resposta |
Resposta de API |
z{Entidade}Params |
Parâmetros de rota |
z{Modulo}Config |
Configuração |
z{Modulo}Ambiente |
Variáveis de ambiente |
PASSO 3 — Derivar o Tipo com z.infer<>
export type tipoUsuarioCriar = z.infer<typeof zUsuarioCriar>
Nunca criar um interface ou type manual separado do schema. O tipo sempre deriva do schema.
PASSO 4 — Implementar safeParse no Ponto de Entrada
Backend (Express)
import { zUsuarioCriar } from "projeto-comuns/tabelas/usuarios"
app.post("/usuarios", async (req, res) => {
const resultado = zUsuarioCriar.safeParse(req.body)
if (!resultado.success) {
return res.status(400).json({ erro: resultado.error.issues[0].message })
}
const usuario = await criarUsuario(resultado.data)
return res.json(usuario)
})
Frontend (Vue)
import { zUsuarioCriar } from "projeto-comuns/tabelas/usuarios"
const salvar = async () => {
const resultado = zUsuarioCriar.safeParse(formulario.value)
if (!resultado.success) {
erros.value = resultado.error.flatten().fieldErrors
return
}
await api.post("/usuarios", resultado.data)
}
Variáveis de ambiente
const zAmbiente = z.object({
DATABASE_URL: z.string().url(),
REDIS_HOST: z.string().default("localhost"),
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
})
// Aqui o throw é desejável — a app não deve subir com env inválido
export const ambiente = () => zAmbiente.parse(process.env)
PASSO 5 — Compor Schemas Derivados
Quando precisar de variações do mesmo schema, compor em vez de duplicar:
// Schema base
const zProduto = z.object({
nome: z.string().trim().min(1),
preco: z.coerce.number().positive(),
categoria: z.string(),
})
// Edição: todos os campos opcionais
const zProdutoEditar = zProduto.partial()
// Resposta: base + campos gerados
const zProdutoResposta = zProduto.extend({
codigo: z.string().uuid(),
criadoEm: z.string().datetime(),
})
// Filtro: subset de campos opcionais
const zProdutoFiltro = zProduto.pick({ categoria: true }).partial()
PASSO 6 — Validar Tipagem
npx tsc --noEmit
Zero erros = concluído.
Checklist Final
- O schema está no pacote
*-comuns(quando compartilhado entre front/back)? - O tipo é derivado do schema com
z.infer<>? - A validação usa
safeParse(exceto em boot/middleware)? - A nomenclatura segue o padrão
z{Entidade}{Acao}? - Transformações (trim, coerce, default) estão no schema, não no handler?
- Schemas derivados usam composição (.partial, .extend, .pick), não duplicação?
tsc --noEmitpassa sem erros?
Referências
- Rule:
.agent/rules/zod-validacao-padrao.global.md - Rule:
.agent/rules/typescript-sem-any.global.md