Schema validation para JSON: validando o formato, não só a sintaxe
JSON válido não é JSON correto. Veja como usar JSON Schema para validar o formato esperado na borda da API, no config e nos contratos entre serviços.
A API aceitou o payload, respondeu 200, e três serviços lá na frente quebraram. O motivo: o front mandou "42" (string) onde o contrato dizia 42 (number), e ninguém checou. O JSON era sintaticamente perfeito — chaves fechadas, vírgulas no lugar, aspas corretas — e mesmo assim estava errado. Essa é a distância entre "isto é JSON válido" e "isto é o JSON que eu esperava", e é exatamente o buraco que schema validation existe para tapar.
Este guia mostra a diferença entre validar sintaxe e validar formato, o que é JSON Schema, onde aplicar a validação e por que um schema compartilhado vale mais que dez if defensivos espalhados pelo código.
Sintaxe válida não é o mesmo que formato correto
Validar sintaxe responde a uma pergunta: "isto é JSON parseável?". Qualquer JSON.parse faz isso e estoura se você esqueceu uma vírgula. Mas ele aceita feliz um objeto com o campo errado, um número onde devia ter texto, ou um email faltando — porque sintaticamente está tudo certo.
Schema validation responde a uma pergunta diferente e mais útil: "este JSON tem a forma que o meu sistema espera?". Os campos certos existem? Os tipos batem? O status é um dos valores permitidos? É a checagem que separa um dado que só parece certo de um que realmente é.
O que é JSON Schema
JSON Schema é um padrão (ele próprio escrito em JSON) para declarar a forma esperada de um documento. Você descreve tipos, campos obrigatórios, valores permitidos, formatos e limites, e valida qualquer payload contra essa descrição.
{
"type": "object",
"required": ["id", "email", "status"],
"properties": {
"id": { "type": "integer", "minimum": 1 },
"email": { "type": "string", "format": "email" },
"status": { "type": "string", "enum": ["active", "inactive"] },
"tags": { "type": "array", "items": { "type": "string" } }
},
"additionalProperties": false
}
Esse schema rejeita o payload do começo do artigo na hora:
{ "id": "42", "email": "joao@", "status": "ligado" }
Três erros de uma vez: id é string e não integer, email não tem formato válido, e status não está no enum. E o melhor: o validador aponta onde cada um falhou, em vez de um genérico "dados inválidos".
Valide na borda, sempre
A regra é simples: todo dado que vem de fora é não-confiável até ser validado, e a validação acontece na borda — no ponto de entrada, antes de qualquer lógica de negócio. Isso vale para:
- Entrada de API — valide o body da request contra o schema antes de processar. Falhe com
400e um erro claro, não com um stack trace três camadas abaixo. - Arquivos de configuração — um schema para o seu
config.jsonpega o typo no nome da chave no startup, não em produção às duas da manhã. - Contratos entre serviços — o serviço A produz, o serviço B consome, e os dois validam contra o mesmo schema. Ninguém precisa adivinhar o formato do outro.
Para checar um payload contra o schema sem subir nada, o validador de JSON Schema faz isso direto no navegador e mostra o caminho exato de cada erro — útil quando o que você quer é só confirmar um contrato antes de codar contra ele.
Tooling: você não escreve o validador
Ninguém implementa validação de schema na mão. Cada linguagem tem uma biblioteca madura: Ajv em JavaScript/TypeScript (rápida, compila o schema), jsonschema em Python, e equivalentes em praticamente todo ecossistema. Elas implementam a especificação, então o mesmo schema funciona em qualquer lado do contrato.
import Ajv from "ajv";
import addFormats from "ajv-formats";
const ajv = addFormats(new Ajv({ allErrors: true }));
const validate = ajv.compile(schema);
if (!validate(payload)) {
// cada erro traz instancePath + message — devolva isso, não um 500
console.log(validate.errors);
}
Note o allErrors: true: por padrão muitos validadores param no primeiro erro. Ligar isso devolve a lista completa de uma vez, o que é muito melhor para quem está do outro lado tentando arrumar o request.
Gerar o schema a partir de um exemplo
Escrever o primeiro schema na mão é chato. Um atalho honesto: pegue um payload de exemplo real e gere o schema inicial a partir dele com uma ferramenta de inferência, depois ajuste — aperte os enum, marque os required, troque um string genérico por format: "email". É muito mais rápido que partir do zero, e te força a olhar campo por campo, que é justamente o ponto. Se você ainda está no degrau anterior — garantindo que o JSON sequer parseia —, vale ver primeiro como validar JSON e os erros de sintaxe mais comuns.
Perguntas frequentes
Qual a diferença entre validar JSON e validar com schema?
Validar JSON checa a sintaxe: se o texto é parseável (vírgulas, aspas, chaves). Validar com schema checa a forma: se os campos, tipos e valores batem com o esperado. Um JSON pode passar na primeira e falhar na segunda — é o caso clássico do número que veio como string. Você quer as duas, nessa ordem.
Onde devo colocar a validação de schema?
Na borda do sistema, antes da lógica de negócio: entrada de API, leitura de config, e em ambos os lados de um contrato entre serviços. A ideia é falhar rápido e com erro claro (caminho + motivo), em vez de deixar um dado torto vazar para dentro e quebrar três camadas depois.
Preciso escrever o validador eu mesmo?
Não, e não deveria. Use bibliotecas que implementam a especificação — Ajv em JS, jsonschema em Python e equivalentes. Elas cobrem os casos extremos da spec que você não quer reimplementar, e o mesmo schema roda igual em todas, o que é o ponto de ter um padrão.
JSON Schema substitui a validação de regra de negócio?
Não. Schema valida estrutura (tipos, obrigatoriedade, formato, faixas) — "este campo é um e-mail bem formado?". Regra de negócio é outra coisa — "este e-mail já está cadastrado?" exige consultar o banco. Use schema para a forma e mantenha as regras de domínio em código separado.
O que levar deste guia
JSON válido não é JSON correto. A sintaxe garante que o texto parseia; o schema garante que ele tem a forma que o seu sistema espera. Declare essa forma uma vez com JSON Schema, valide todo dado que vem de fora na borda, e compartilhe o mesmo schema entre quem produz e quem consome. Use uma biblioteca pronta, ligue allErrors, e devolva o erro com caminho e motivo. É a diferença entre pegar o problema no 400 da entrada ou caçá-lo num 500 três serviços depois.
- 01 Salt, pepper, bcrypt e Argon2id: como proteger senhas de verdade Em 2012, o LinkedIn expôs 117mi de senhas. SHA-1 sem salt — 90% quebradas em 4h. Entenda o que cada camada de proteção resolve e por que Argon2id é a escolha certa hoje.
- 02 Licenças MIT, Apache e GPL: diferenças práticas para devs MIT, Apache 2.0 e GPL não são a mesma coisa. Entenda permissiva vs copyleft, proteção de patentes e compatibilidade antes de colocar código em produção.