JSON Schema descreve a estrutura e as regras de documentos JSON. Em aplicações Node.js, ele pode validar requisições, respostas, eventos, configurações e contratos. Um schema bem definido reduz verificações manuais, gera mensagens consistentes e ajuda ferramentas como OpenAPI, Ajv e geradores de tipos.
Schema básico
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/order.json",
"type": "object",
"required": ["customerId", "items"],
"properties": {
"customerId": {
"type": "string",
"format": "uuid"
},
"items": {
"type": "array",
"minItems": 1,
"maxItems": 100,
"items": {
"$ref": "#/$defs/item"
}
}
},
"additionalProperties": false,
"$defs": {
"item": {
"type": "object",
"required": ["productId", "quantity"],
"properties": {
"productId": { "type": "string", "format": "uuid" },
"quantity": { "type": "integer", "minimum": 1, "maximum": 1000 }
},
"additionalProperties": false
}
}
}Ajv
npm install ajv ajv-formatsimport Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv({
allErrors: true,
removeAdditional: false,
useDefaults: false,
coerceTypes: false,
});
addFormats(ajv);
const validateOrder = ajv.compile(orderSchema);Compile schemas no startup, não em cada requisição. Falhe cedo se o contrato estiver inválido.
Validando uma requisição
function validateBody(validate) {
return (req, res, next) => {
if (!validate(req.body)) {
res.status(400).json({
error: 'validation_failed',
details: validate.errors.map((item) => ({
path: item.instancePath,
keyword: item.keyword,
message: item.message,
})),
});
return;
}
next();
};
}Não devolva o schema inteiro ou dados sensíveis nos erros.
additionalProperties
false impede campos desconhecidos e detecta erros de digitação. Em APIs públicas, isso pode dificultar evolução se clientes enviarem campos futuros. Defina a política de compatibilidade conscientemente.
Required e nullable
Um campo pode ser opcional, obrigatório ou aceitar null. Para aceitar null:
{
"type": ["string", "null"]
}Não confunda ausência com null. Eles podem ter significados diferentes em PATCH.
String
Use minLength, maxLength, pattern e format. Formatos podem exigir plugin e não substituem validação de negócio.
Números
Use integer, minimum, maximum e multipleOf. Valores monetários devem evitar erros de ponto flutuante; prefira centavos inteiros ou decimal adequado.
Arrays
Defina minItems, maxItems e uniqueItems quando necessário. Limites evitam payloads enormes e trabalho excessivo.
oneOf, anyOf e allOf
oneOf exige exatamente uma alternativa válida; anyOf, pelo menos uma; allOf combina regras. Modelos excessivamente complexos podem gerar erros difíceis e validação lenta.
Discriminador
Um campo type pode selecionar variantes:
{
"oneOf": [
{ "$ref": "#/$defs/cardPayment" },
{ "$ref": "#/$defs/pixPayment" }
]
}Garanta que as variantes sejam mutuamente exclusivas.
if, then e else
Regras condicionais permitem exigir campos conforme outro atributo. Use para estrutura, mas deixe regras de domínio complexas no código.
$ref
Referências reutilizam schemas. Controle URLs e carregamento remoto; não busque um schema arbitrário fornecido pelo usuário.
$id e versionamento
Use IDs estáveis e uma estratégia de versão. Alterar um schema publicado sem mudar a identidade pode quebrar consumidores e caches.
Coerção de tipos
Ajv pode converter strings em números ou booleanos. Isso é conveniente para query strings, mas pode esconder entrada incorreta. Para body JSON, prefira tipos explícitos.
Defaults
Aplicar defaults durante validação modifica o objeto. Documente esse comportamento e não use defaults para campos de segurança como role, tenant ou owner.
removeAdditional
Remover campos silenciosamente pode esconder erro do cliente e permitir confusão. Em APIs, geralmente é melhor rejeitar ou preservar conforme contrato.
Validação de resposta
Validar respostas em testes e staging detecta regressões:
if (!validateResponse(payload)) {
logger.error({ errors: validateResponse.errors }, 'Resposta fora do contrato');
throw new InternalError();
}Em produção, avalie overhead e nunca devolva conteúdo parcial inválido.
Schema de configuração
Valide variáveis processadas no startup:
const config = {
port: Number(process.env.PORT),
environment: process.env.NODE_ENV,
};
if (!validateConfig(config)) {
throw new Error('Configuração inválida');
}Eventos e filas
Inclua schemaVersion em mensagens. Consumidores devem tratar versões conhecidas e enviar inválidas para dead letter sem loop infinito.
Segurança
Validação de schema não elimina SQL injection, XSS, path traversal ou regras de autorização. Ela garante forma e limites iniciais; o código ainda precisa normalizar e usar APIs seguras.
ReDoS em pattern
Regex complexas em pattern podem consumir CPU. Use expressões simples, limite tamanho da string e teste entradas adversariais.
Mensagens de erro
Traduza erros para um formato estável. O caminho deve apontar o campo, mas não revelar implementação interna ou valores secretos.
TypeScript
Tipos compilados não validam dados externos em runtime. Gere tipos a partir do schema ou schema a partir dos tipos, mas mantenha uma fonte de verdade e teste divergências.
OpenAPI
OpenAPI 3.1 usa vocabulário próximo do JSON Schema. Ainda assim, ferramentas podem suportar subconjuntos diferentes. Execute lint e testes com o ecossistema escolhido.
Erros comuns
- compilar schema em cada request;
- sem limites de tamanho;
- coerção silenciosa;
- additionalProperties permissivo sem intenção;
- regex vulnerável;
- usar tipos TypeScript como validação;
- buscar referências remotas não confiáveis;
- schema e OpenAPI divergentes;
- expor valores nos erros.
Fluxo recomendado
- escolha o draft;
- defina IDs e versões;
- aplique limites;
- compile no startup;
- valide entrada na borda;
- mantenha coerção explícita;
- teste schemas de resposta;
- integre ao OpenAPI;
- monitore falhas.
Combine JSON Schema com OpenAPI no Node.js, Zod no TypeScript, Tratamento de Erros e Rate Limiting.
Consulte a especificação oficial JSON Schema e a documentação oficial do Ajv.



