OpenAPI com Node.js permite descrever uma API HTTP em um contrato legível por pessoas e ferramentas. O documento informa rotas, métodos, parâmetros, autenticação, schemas, respostas e exemplos. A partir dele, equipes podem gerar documentação, clientes, mocks, validações e testes de compatibilidade.
Uma especificação útil precisa acompanhar o comportamento real. Um arquivo bonito, mas desatualizado, aumenta erros e reduz confiança. Neste guia, você aprenderá a estruturar o contrato, reutilizar componentes, validar requisições e respostas e integrar a especificação ao pipeline. Para revisar conceitos básicos, leia o que é API e como criar uma API com Node.js.
O que é a especificação OpenAPI?
OpenAPI é um formato independente de linguagem para APIs HTTP. O documento pode ser escrito em YAML ou JSON. Ele não executa a aplicação, mas define o contrato que clientes e servidores devem seguir. Ferramentas utilizam essa descrição para criar interfaces interativas, SDKs e verificações automáticas.
O contrato deve representar decisões reais: quais campos são obrigatórios, quais formatos são aceitos, quais erros podem ocorrer e quais credenciais são necessárias. Evite descrições vagas como “objeto qualquer” ou respostas sem schema.
Estrutura inicial
openapi: 3.1.0
info:
title: Orders API
version: 1.0.0
description: API para gerenciamento de pedidos
servers:
- url: https://api.example.com
description: Produção
paths:
/orders/{orderId}:
get:
operationId: getOrder
summary: Busca um pedido
parameters:
- $ref: '#/components/parameters/OrderId'
responses:
'200':
description: Pedido encontrado
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
'404':
$ref: '#/components/responses/NotFound'
components: {}Use operationId único e estável. Geradores de cliente frequentemente o transformam no nome do método. Alterá-lo pode quebrar integrações mesmo que a rota permaneça igual.
Definindo schemas reutilizáveis
components:
schemas:
Order:
type: object
additionalProperties: false
required:
- id
- customerId
- totalCents
- status
- createdAt
properties:
id:
type: string
format: uuid
customerId:
type: string
format: uuid
totalCents:
type: integer
minimum: 0
status:
type: string
enum: [pending, paid, cancelled]
createdAt:
type: string
format: date-time
Error:
type: object
additionalProperties: false
required: [code, requestId]
properties:
code:
type: string
message:
type: string
requestId:
type: stringadditionalProperties: false documenta que campos extras não pertencem ao contrato. Decida se o servidor realmente os rejeitará ou apenas os ignorará; documentação e implementação devem concordar.
Parâmetros
Parâmetros podem aparecer em path, query, header ou cookie. Parâmetros de path são sempre obrigatórios. Defina limites e formatos.
components:
parameters:
OrderId:
name: orderId
in: path
required: true
description: Identificador do pedido
schema:
type: string
format: uuid
PageSize:
name: pageSize
in: query
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 20Não documente paginação sem explicar cursor, ordenação e estabilidade. Clientes precisam saber como obter a próxima página e quando um cursor expira.
Corpo de requisição
paths:
/orders:
post:
operationId: createOrder
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOrderInput'
examples:
basic:
value:
customerId: 4f583f15-4387-4f38-91ca-a657dc61980f
items:
- productId: SKU-123
quantity: 2
responses:
'201':
description: Pedido criado
headers:
Location:
schema:
type: string
description: URL do novo recurso
content:
application/json:
schema:
$ref: '#/components/schemas/Order'Exemplos ajudam consumidores e testes, mas não substituem schema. Mantenha exemplos válidos e sem dados reais ou segredos.
Documentando erros
Defina um formato consistente para erros. A resposta deve ter código estável, requestId e detalhes controlados. Não exponha stack trace ou mensagens do banco.
components:
responses:
NotFound:
description: Recurso não encontrado
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: ORDER_NOT_FOUND
message: Pedido não encontrado
requestId: req_01JABC
ValidationError:
description: Dados inválidos
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/Error'
- type: object
properties:
fields:
type: array
items:
type: object
required: [path, code]
properties:
path:
type: string
code:
type: stringDocumente 400, 401, 403, 404, 409, 429 e 5xx quando realmente aplicáveis. Não liste todo status possível apenas para parecer completo.
Autenticação e autorização
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- bearerAuth: []O esquema informa como enviar a credencial, mas não descreve sozinho as permissões. Em cada operação, explique escopos, papéis ou regras de propriedade do recurso. Para tokens, consulte JWT seguro no Node.js.
Validação no servidor
O documento pode alimentar um middleware de validação. A ferramenta deve rejeitar parâmetros e bodies inválidos antes do caso de uso e, em testes ou homologação, validar também respostas.
import express from 'express';
import OpenApiValidator from 'express-openapi-validator';
const app = express();
app.use(express.json({ limit: '100kb' }));
app.use(OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true,
validateResponses: process.env.NODE_ENV !== 'production',
validateSecurity: false
}));A opção de segurança deve ser integrada à autenticação real. Não habilite ou desabilite sem entender o comportamento da biblioteca. Validação de resposta em produção pode ter custo e transformar um erro de implementação em falha para o cliente; teste o impacto.
Tratando erros de validação
app.use((error, req, res, next) => {
if (error.status && Array.isArray(error.errors)) {
return res.status(error.status).json({
code: 'INVALID_REQUEST',
requestId: req.id,
fields: error.errors.map(item => ({
path: item.path,
code: item.errorCode ?? 'INVALID'
}))
});
}
next(error);
});Não envie o objeto original sem revisão. Bibliotecas podem incluir detalhes internos ou valores recebidos. Normalize a resposta no padrão público da API.
Design-first ou code-first?
No design-first, a equipe cria e revisa o contrato antes da implementação. Isso facilita colaboração com consumidores e geração antecipada de mocks. No code-first, decorators ou schemas do código geram a especificação, reduzindo duplicação.
As duas abordagens podem funcionar. O requisito é ter uma fonte principal clara, revisão de mudanças e verificação automática de divergência. Evite editar o YAML e schemas do código independentemente.
JSON Schema e OpenAPI 3.1
OpenAPI 3.1 está mais alinhado ao JSON Schema moderno. Ainda assim, ferramentas podem ter níveis diferentes de suporte. Valide a compatibilidade do ecossistema antes de usar recursos avançados.
Para schemas executáveis no código, consulte Zod no TypeScript. É possível gerar OpenAPI a partir de Zod ou gerar tipos a partir do contrato, mas revise a fidelidade da conversão.
Lint e governança
Um linter aplica regras como operationId obrigatório, descrições mínimas, formatos de erro e tags. Isso evita que cada equipe documente de uma forma.
npx @stoplight/spectral-cli lint openapi.yamlCrie um conjunto pequeno de regras úteis. Regras excessivas geram exceções e reduzem adesão. Diferencie erro que bloqueia o merge de aviso para melhoria.
Detecção de breaking changes
Remover rota, campo, enum ou status pode quebrar clientes. Compare a especificação proposta com a versão publicada no pipeline. Mudanças aparentemente pequenas, como tornar um campo obrigatório, são incompatíveis.
- adicionar campo opcional costuma ser compatível;
- remover campo é incompatível;
- reduzir enum pode quebrar clientes;
- mudar tipo ou formato é incompatível;
- adicionar requisito de autenticação é incompatível;
- alterar semântica exige nova versão ou migração.
Geração de clientes
Geradores podem criar SDKs para TypeScript, Java, Kotlin e outras linguagens. Fixe a versão da ferramenta, revise o código e publique pacotes versionados. Não force consumidores a regenerar o cliente a cada deploy sem mudanças de contrato.
npx openapi-typescript openapi.yaml \
--output src/generated/api-types.tsTipos gerados ajudam o compilador, mas respostas de rede ainda precisam de confiança no servidor ou validação no cliente quando o risco justificar.
Mocks
Um mock baseado em OpenAPI permite que frontend e integrações avancem antes do backend. Use exemplos realistas e inclua respostas de erro. Não trate o mock como prova de que a implementação final está correta.
Testes de contrato
Teste a implementação contra a especificação. Envie requisições válidas e inválidas, valide status, headers e schemas. Em integração, percorra operações críticas e confirme que exemplos continuam aceitos.
import test from 'node:test';
import assert from 'node:assert/strict';
import request from 'supertest';
test('rejeita pageSize acima do limite', async () => {
const response = await request(app)
.get('/orders?pageSize=1000')
.set('Authorization', `Bearer ${token}`);
assert.equal(response.status, 400);
assert.equal(response.body.code, 'INVALID_REQUEST');
});Veja também testes unitários com Jest.
Versionamento
Versione o contrato junto do código e publique uma cópia imutável para consumidores. A versão em info.version descreve o documento, mas a estratégia de versão da API precisa ser definida separadamente.
Prefira evoluções compatíveis. Uma nova versão principal custa manutenção, documentação e migração de clientes.
Segurança da documentação
- não inclua tokens ou dados reais em exemplos;
- separe servidores por ambiente;
- não publique rotas internas sem necessidade;
- proteja a interface interativa em ambientes privados;
- limite “Try it out” quando houver operações sensíveis;
- revise descrições para não expor infraestrutura;
- mantenha schemas de autenticação corretos;
- faça scan de segredos no repositório.
Observabilidade
Use operationId como dimensão estável em logs, métricas e traces. Isso agrupa chamadas pela operação documentada, evitando cardinalidade de URLs com IDs. Meça quantidade de falhas de validação e campos mais problemáticos para melhorar clientes e documentação.
Erros comuns
- Gerar documentação uma vez: o contrato rapidamente fica desatualizado;
- Usar schemas genéricos: ferramentas não conseguem validar nem gerar clientes;
- Não documentar erros: consumidores dependem de mensagens instáveis;
- Exemplos inválidos: testes e mocks ensinam uso incorreto;
- Sem breaking-change check: alterações incompatíveis chegam silenciosamente;
- Confiar apenas no TypeScript: dados HTTP continuam não confiáveis.
Checklist
- operationId é único e estável;
- parâmetros possuem limites e formatos;
- schemas definem required e additionalProperties;
- erros seguem um formato comum;
- autenticação está documentada;
- requests são validados;
- lint e breaking changes rodam no CI;
- exemplos e clientes são testados.
Referências oficiais
Conclusão
OpenAPI com Node.js transforma o comportamento HTTP em um contrato que pode ser revisado e automatizado. Schemas precisos, operationIds estáveis, erros documentados e exemplos válidos melhoram a experiência de quem produz e consome a API.
Integre a especificação ao código e ao pipeline: valide requisições, faça lint, detecte mudanças incompatíveis e execute testes de contrato. Dessa forma, a documentação deixa de ser um arquivo auxiliar e passa a ser uma parte verificável da arquitetura.



