Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Ajv no Node.js: JSON Schema

Atualizado em: 2 de setembro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

Usar Ajv no Node.js permite validar objetos e documentos JSON com JSON Schema ou JSON Type Definition. A biblioteca compila schemas em funções JavaScript eficientes, que podem ser reutilizadas em APIs, filas, arquivos de configuração, webhooks e contratos entre serviços.

JSON Schema é independente de linguagem e pode ser compartilhado com gateways, clientes, testes e documentação. Porém, a implementação segura exige strict mode, schemas compilados uma vez, limites de tamanho, rejeição de campos desconhecidos, formatos configurados explicitamente e cuidado com coerção ou remoção automática de dados.

Neste guia, você aprenderá a instalar Ajv 8, criar schemas, compilar validators, formatar erros, integrar com TypeScript, validar APIs, combinar schemas, usar referências, gerar código standalone e testar contratos.

O que é Ajv?

Ajv é um validador de JSON Schema e JTD. A documentação oficial do Ajv explica que schemas são compilados e armazenados em cache. A documentação oficial do JSON Schema apresenta drafts e palavras-chave do padrão.

Quando usar?

  • APIs REST;
  • webhooks;
  • mensagens de fila;
  • arquivos de configuração;
  • contratos entre linguagens;
  • validação em gateways;
  • OpenAPI;
  • eventos versionados.

Ajv versus Zod

Ajv trabalha com JSON Schema ou JTD, padrões que podem ser consumidos fora do TypeScript. Zod cria schemas em código TypeScript com ótima inferência. O site já possui o artigo Zod no TypeScript: Validação de Dados.

  • Ajv: contrato independente de linguagem e compilação eficiente.
  • Zod: experiência TypeScript direta e transforms em código.

Instalando

npm install ajv ajv-formats

O pacote de formats adiciona validações como e-mail, URI e date-time conforme a configuração.

Instância básica

import Ajv from 'ajv';
import addFormats from 'ajv-formats';

const ajv = new Ajv({
  allErrors: true,
  strict: true
});

addFormats(ajv);

strict ajuda a detectar keywords desconhecidas e ambiguidades no schema.

Schema de usuário

const CreateUserSchema = {
  $id: 'CreateUser',
  type: 'object',
  additionalProperties: false,
  required: ['name', 'email'],
  properties: {
    name: {
      type: 'string',
      minLength: 1,
      maxLength: 100
    },
    email: {
      type: 'string',
      format: 'email',
      maxLength: 254
    },
    age: {
      type: 'integer',
      minimum: 18,
      maximum: 150
    }
  }
};

Compilando

const validateCreateUser = ajv.compile(
  CreateUserSchema
);

Compile no startup e reutilize. Compilar em toda requisição desperdiça CPU e pode aumentar latência.

Validando

const valid = validateCreateUser(input);

if (!valid) {
  throw new ValidationError(
    formatValidationErrors(
      validateCreateUser.errors
    )
  );
}

A propriedade errors é sobrescrita na próxima execução da função. Copie os dados quando precisar preservá-los.

additionalProperties

Definir false rejeita campos não declarados. Isso reduz mass assignment:

{
  "name": "Ana",
  "email": "ana@example.com",
  "isAdmin": true
}

Sem uma política clara, campos inesperados podem chegar a repositories ou modelos.

required

Uma propriedade em properties não é obrigatória automaticamente. Inclua em required.

String vazia

type: string aceita string vazia. Use minLength quando o campo precisa de conteúdo.

Inteiros

Use integer para valores sem fração. Ainda defina limites de negócio.

Arrays

tags: {
  type: 'array',
  minItems: 0,
  maxItems: 20,
  uniqueItems: true,
  items: {
    type: 'string',
    minLength: 1,
    maxLength: 50
  }
}

Limitar quantidade impede payloads enormes e trabalho excessivo.

Objetos aninhados

address: {
  type: 'object',
  additionalProperties: false,
  required: ['city', 'country'],
  properties: {
    city: { type: 'string', maxLength: 100 },
    country: {
      type: 'string',
      pattern: '^[A-Z]{2}$'
    }
  }
}

Regex e ReDoS

Patterns precisam ser simples e limitados. Consulte ReDoS no Node.js.

Formatos

Formats não significam automaticamente que o valor é seguro para uso. Uma URL válida ainda pode apontar para rede privada.

Consulte SSRF no Node.js.

date-time

createdAt: {
  type: 'string',
  format: 'date-time'
}

Depois da validação, converta para Date e confirme regras como intervalo e fuso quando necessário.

Enum

status: {
  enum: ['draft', 'active', 'disabled']
}

Use enums explícitos em vez de strings livres.

const

eventType: {
  const: 'user.created'
}

É útil em envelopes de eventos.

oneOf

oneOf: [
  { $ref: '#/$defs/cardPayment' },
  { $ref: '#/$defs/pixPayment' }
]

Os schemas precisam ser mutuamente claros. Alternativas que validam simultaneamente criam ambiguidades.

Discriminator

Uma propriedade como type pode selecionar a variante. Confira suporte e configuração da versão usada.

$defs

$defs: {
  uuid: {
    type: 'string',
    format: 'uuid'
  }
}

Reutilize definições dentro do schema.

$ref

userId: {
  $ref: '#/$defs/uuid'
}

Referências evitam duplicação, mas schemas cíclicos e remotos precisam de governança.

Registrando schemas

ajv.addSchema(UserSchema);
ajv.addSchema(AddressSchema);

const validate = ajv.getSchema('CreateUser');

Use IDs únicos e versionados quando houver vários contratos.

Schemas remotos

Não permita que um schema não confiável faça a aplicação buscar URLs arbitrárias. Carregue contratos de uma origem aprovada ou bundle local.

SSRF em loadSchema

Uma função de carregamento remoto precisa de allowlist e limites. A opção mais segura é empacotar schemas no deploy.

allErrors

Com true, Ajv coleta vários erros, melhorando UX. Porém, payloads enormes podem aumentar trabalho. Combine com body limits.

Primeiro erro

Em endpoints de alto volume, parar no primeiro erro pode ser suficiente. Meça o custo.

strict mode

Strict mode ajuda a detectar:

  • keywords desconhecidas;
  • tipos contraditórios;
  • tuplas sem limites;
  • formats indefinidos;
  • schemas ambíguos.

Não desabilite globalmente apenas para silenciar um schema mal definido.

Coerção de tipos

const ajv = new Ajv({
  coerceTypes: true
});

Essa opção pode transformar strings em números ou booleanos. Ela facilita query parameters, mas também muda o input. Prefira conversão explícita em contratos sensíveis.

removeAdditional

Ajv pode remover campos desconhecidos. Rejeitar é frequentemente mais transparente, pois o cliente descobre o erro. Remoção silenciosa pode esconder tentativas e bugs.

useDefaults

Defaults podem ser aplicados durante validação. Isso modifica o objeto. Em APIs, considere criar um objeto novo depois de validar.

Validação pura

Manter validators sem mutação simplifica testes e auditoria. Normalize em uma etapa separada.

TypeScript

import type { JSONSchemaType } from 'ajv';

interface UserInput {
  name: string;
  email: string;
}

const schema: JSONSchemaType<UserInput> = {
  type: 'object',
  additionalProperties: false,
  required: ['name', 'email'],
  properties: {
    name: { type: 'string', maxLength: 100 },
    email: { type: 'string', format: 'email' }
  }
};

A tipagem ajuda a alinhar schema e interface, mas a validação runtime continua sendo a fonte para dados externos.

Type guard

Depois de uma validação tipada bem-sucedida, TypeScript pode estreitar o tipo conforme a API utilizada.

API Express

function validateBody(validate) {
  return (req, res, next) => {
    if (!validate(req.body)) {
      return res.status(400).json({
        code: 'VALIDATION_ERROR',
        errors: formatErrors(validate.errors)
      });
    }

    next();
  };
}

Fastify

Fastify usa JSON Schema em rotas e compiladores. Ajv pode ser configurado conforme a integração, mas alterações globais devem ser testadas.

Consulte Fastify com Node.js.

Hono

Crie um middleware que valida o body e coloca o resultado no context. Consulte Hono no Node.js.

NestJS

Nest pode usar pipes customizados para Ajv. Avalie se JSON Schema precisa ser o contrato central. Veja NestJS no Node.js.

tRPC

tRPC costuma usar validators TypeScript-first, mas também aceita adapters compatíveis. Consulte tRPC no Node.js.

OpenAPI

OpenAPI usa Schema Object semelhante, mas não é idêntico a todos os drafts JSON Schema. Confirme compatibilidade e conversão.

Consulte OpenAPI com Node.js.

Mensagens de fila

Valide o envelope antes de processar:

{
  "messageId": "...",
  "type": "invoice.created",
  "version": 2,
  "data": {}
}

Mensagens inválidas devem ir para DLQ ou fluxo controlado, não causar retry infinito.

Eventos versionados

Mantenha schema por versão e upcasters quando necessário. Consulte Event Sourcing no Node.js.

Webhooks

Primeiro valide assinatura nos bytes brutos; depois parseie e valide o JSON. Consulte Assinaturas HMAC no Node.js.

Formato de erros

function formatErrors(errors = []) {
  return errors.map(error => ({
    path: error.instancePath || '/',
    keyword: error.keyword,
    message: error.message
  }));
}

Não retorne schema interno completo ou valores sensíveis.

Localização de mensagens

O servidor pode retornar códigos e paths, enquanto a interface traduz. Bibliotecas adicionais oferecem mensagens localizadas.

Dados nos erros

Não inclua o valor rejeitado quando ele pode ser senha, token ou dado pessoal.

Keywords customizadas

Ajv permite extensões, mas keywords executam código durante validação. Mantenha implementações pequenas, puras e testadas.

Async validation

É possível validar com operações assíncronas, porém consultas de banco dentro do schema misturam contrato e regra de negócio. Prefira duas etapas.

Validação sintática e de domínio

  • Schema: tipo, formato, tamanho e estrutura.
  • Domínio: e-mail único, saldo, estado do pedido, permission.

Standalone code

Ajv pode gerar validators standalone, úteis em ambientes com restrições de eval ou para reduzir compilação no startup.

Content Security Policy

Alguns ambientes proíbem geração dinâmica de funções. Use código standalone ou configuração compatível.

Compilação no build

Gerar validators no CI detecta schemas inválidos antes do deploy.

Performance

Compile uma vez e reutilize. Não crie uma nova instância Ajv por request.

Cache de schemas

Ajv mantém validators compilados. Use o mesmo objeto de schema ou IDs registrados.

Payload grande

A validação não substitui body limit. Rejeite o corpo antes de JSON.parse quando ultrapassar o máximo.

Profundidade

Objetos extremamente aninhados podem consumir recursos. Limite profundidade no contrato ou parser quando necessário.

Prototype Pollution

Rejeite campos desconhecidos e atualize bibliotecas. Consulte Prototype Pollution no Node.js.

Logs

Registre schema ID, rota, quantidade de erros e request ID. Não registre o payload completo.

Consulte Logs com Pino no Node.js.

Métricas

Monitore rejeições por schema, keyword, versão, rota e duração. Evite path dinâmico como label de alta cardinalidade.

Auditoria

Alterações em schemas críticos devem ter revisão, versão e histórico. Mudanças de contrato podem afetar parceiros e filas.

Teste válido

test('aceita usuário válido', () => {
  const input = {
    name: 'Ana',
    email: 'ana@example.com'
  };

  assert.equal(validateCreateUser(input), true);
});

Teste de campo extra

Envie isAdmin e confirme rejeição.

Teste de limites

Teste exatamente mínimo, máximo, abaixo e acima.

Teste de arrays

Inclua vazios, duplicados, muitos itens e item inválido.

Teste de versões

Mantenha fixtures válidas e inválidas para cada versão de evento.

Teste de schema inválido

O startup ou build deve falhar se o schema possui keyword incorreta.

Property-based testing

Gere objetos aleatórios para verificar que valores fora do contrato nunca são aceitos ou causam exceção inesperada.

Erros comuns

  • Compilar por request: CPU é desperdiçada.
  • additionalProperties aberto: campos inesperados passam.
  • Desabilitar strict mode: erros de schema são ignorados.
  • Coerção silenciosa: input muda sem clareza.
  • Format tratado como segurança: URL válida pode ser SSRF.
  • Erro com valor: dados sensíveis vazam.
  • Schema remoto livre: aplicação busca destinos arbitrários.

Boas práticas

  • Use Ajv 8 e strict mode.
  • Compile no startup ou build.
  • Reutilize validators.
  • Feche additionalProperties.
  • Defina limites.
  • Evite mutação automática.
  • Versione schemas.
  • Separe regra de domínio.
  • Formate erros com segurança.
  • Teste bordas e versões.

Conclusão

Usar Ajv no Node.js oferece validação eficiente e contratos JSON independentes de linguagem. Schemas compilados podem proteger APIs, webhooks, filas e arquivos com o mesmo padrão.

A implementação segura usa strict mode, limites e campos fechados, sem confiar em coerção ou formats como barreira completa. Com schemas versionados, validators reutilizados e testes de contrato, Ajv mantém dados externos previsíveis antes que alcancem a regra de negócio.

10 melhores cursos de programação em 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