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

Versionamento de API no Node.js

Atualizado em: 21 de agosto de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

O versionamento de API no Node.js permite evoluir contratos HTTP sem interromper clientes que ainda dependem do comportamento antigo. Quando uma rota muda nomes de campos, regras de validação, códigos de status ou formato de resposta, consumidores móveis, integrações externas e serviços internos podem quebrar mesmo que o servidor continue funcionando.

Uma estratégia de versão define como alterações incompatíveis são introduzidas, quanto tempo versões antigas permanecem disponíveis, como clientes escolhem o contrato desejado e como a equipe acompanha adoção. O objetivo não é criar uma versão para cada pequena mudança, mas estabelecer previsibilidade para mudanças que realmente quebram compatibilidade.

Neste guia, você aprenderá a escolher entre versão na URL, header ou media type, organizar rotas, compartilhar regras, documentar contratos, depreciar versões antigas, medir uso e testar compatibilidade em aplicações Node.js.

O que significa versionar uma API?

Versionar uma API significa identificar explicitamente uma geração do contrato público. A versão deve representar o conjunto de recursos, campos, comportamentos e garantias que o cliente pode esperar.

A especificação HTTP Semantics RFC 9110 descreve fundamentos de métodos, headers e respostas. A especificação OpenAPI permite documentar contratos versionados de forma legível por ferramentas.

Para documentação prática, consulte OpenAPI com Node.js. Para estruturar servidores rápidos e validados, veja Fastify com Node.js.

Quando uma nova versão é necessária?

Uma nova versão costuma ser necessária quando uma alteração quebra clientes existentes. Exemplos:

  • remover ou renomear um campo;
  • alterar o tipo de um valor;
  • tornar obrigatório um campo antes opcional;
  • mudar significado de um status HTTP;
  • alterar regras de autenticação;
  • mudar paginação ou ordenação padrão;
  • remover um endpoint;
  • alterar formato de erros;
  • trocar unidade ou precisão de números.

Adicionar um campo opcional geralmente é compatível, desde que clientes ignorem propriedades desconhecidas. Ainda assim, consumidores rígidos podem falhar, portanto contratos precisam documentar essa expectativa.

Mudança compatível versus incompatível

Uma mudança compatível amplia o contrato sem invalidar o uso anterior. Uma mudança incompatível exige adaptação do consumidor.

// Resposta anterior
{
  "id": 42,
  "name": "Ana"
}

// Alteração geralmente compatível
{
  "id": 42,
  "name": "Ana",
  "createdAt": "2026-08-21T12:00:00.000Z"
}

Já mudar id de número para string pode quebrar validações, caches e bancos dos clientes.

Versão na URL

A estratégia mais visível coloca a versão no caminho:

GET /api/v1/users
GET /api/v2/users

Ela é simples de entender, observar e rotear. Logs, caches e documentação deixam a versão explícita.

Exemplo com Express

const express = require('express');
const app = express();

const v1Router = express.Router();
const v2Router = express.Router();

v1Router.get('/users/:id', getUserV1);
v2Router.get('/users/:id', getUserV2);

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

Consulte Express 5 e erros assíncronos para tratamento centralizado de falhas.

Versão por header

Outra estratégia usa um header próprio:

GET /api/users/42
X-API-Version: 2

A URL permanece estável, mas a versão fica menos visível. Ferramentas, caches e proxies precisam considerar o header.

Implementação por header

app.use((req, res, next) => {
  const version = req.get('x-api-version') || '1';

  if (!['1', '2'].includes(version)) {
    return res.status(400).json({
      code: 'UNSUPPORTED_API_VERSION',
      message: 'Versão de API não suportada'
    });
  }

  req.apiVersion = version;
  next();
});

Use allowlist e um valor padrão documentado. Não aceite qualquer string e tente carregar módulos dinamicamente.

Versionamento por media type

O cliente pode pedir uma representação específica:

Accept: application/vnd.example.user-v2+json

Esse modelo é alinhado à negociação de conteúdo, mas aumenta complexidade operacional e pode confundir consumidores.

Qual estratégia escolher?

  • URL: melhor visibilidade e simplicidade operacional.
  • Header: URL estável, mas exige atenção a caches.
  • Media type: útil para representações sofisticadas, porém mais complexo.

Para APIs públicas, a URL costuma ser a opção mais fácil de comunicar. Para serviços internos com gateway bem controlado, headers podem funcionar bem.

Não use versão de pacote como versão de API

A versão do servidor pode mudar diariamente sem alterar o contrato. API v2 não precisa corresponder ao pacote 2.0.0. Mantenha os conceitos separados:

  • versão da aplicação;
  • versão do contrato HTTP;
  • versão do schema de banco;
  • versão do SDK cliente.

Organizando o código

src/
├── api/
│   ├── v1/
│   │   ├── routes.js
│   │   └── serializers.js
│   └── v2/
│       ├── routes.js
│       └── serializers.js
├── domain/
└── repositories/

Compartilhe domínio e persistência quando as regras forem iguais. Separe serializers, validações e controladores que realmente diferem.

Evite copiar toda a aplicação

Duplicar todos os arquivos para criar v2 aumenta bugs e manutenção. Prefira uma camada de adaptação:

function serializeUserV1(user) {
  return {
    id: user.id,
    name: user.fullName
  };
}

function serializeUserV2(user) {
  return {
    id: String(user.id),
    profile: {
      displayName: user.fullName
    }
  };
}

O domínio continua único; apenas a representação pública muda.

Validação por versão

const schemas = {
  v1: userInputV1Schema,
  v2: userInputV2Schema
};

function validateInput(version, body) {
  return schemas[version].parse(body);
}

Uma versão não deve reutilizar silenciosamente o schema da outra quando os contratos divergem.

Formato de erros

Padronize erros dentro de cada versão:

{
  "code": "INVALID_EMAIL",
  "message": "E-mail inválido",
  "details": [
    {
      "field": "email",
      "reason": "format"
    }
  ]
}

Alterar o formato de erro também é alteração de contrato.

Autenticação e autorização

Uma nova versão não deve diminuir segurança. Tokens precisam continuar validados por issuer, audience, expiração e escopos. Consulte JWT seguro no Node.js e OAuth 2.0 com PKCE.

Documentação separada

Mantenha uma especificação OpenAPI para cada contrato ativo:

openapi/
├── v1.yaml
└── v2.yaml

O portal deve indicar versão recomendada, data de depreciação e guia de migração.

Guia de migração

Um guia útil mostra:

  • campos removidos;
  • campos renomeados;
  • novos requisitos;
  • mudanças de status;
  • exemplos antes e depois;
  • prazo para migração;
  • como testar em sandbox.

Depreciação

Não remova uma versão sem aviso. Defina ciclo:

  1. anunciar a nova versão;
  2. marcar a antiga como depreciada;
  3. medir clientes restantes;
  4. enviar alertas;
  5. bloquear novas integrações na antiga;
  6. desativar após o prazo.

Headers de depreciação

Respostas podem incluir informações de depreciação e links para documentação, conforme padrões suportados pelo seu ecossistema:

Deprecation: true
Sunset: Sat, 31 Jan 2027 23:59:59 GMT
Link: <https://docs.example.com/migrate-v2>; rel="successor-version"

Clientes nem sempre processam esses headers, então combine com comunicação direta.

Métricas por versão

Registre métricas de:

  • requisições por versão;
  • clientes ativos;
  • taxa de erro;
  • latência;
  • rotas mais usadas;
  • respostas de depreciação;
  • versão desconhecida.

Não use identificadores pessoais como labels de alta cardinalidade.

Logs

logger.info({
  apiVersion: req.apiVersion,
  route: req.route.path,
  statusCode: res.statusCode
}, 'request_completed');

Evite registrar tokens, corpos e dados pessoais.

Cache

Se a versão está em header, configure Vary:

Vary: X-API-Version

Sem isso, um cache pode entregar resposta v1 para cliente v2. Com versão na URL, a chave já tende a ser diferente.

CORS

Headers próprios podem exigir inclusão em Access-Control-Allow-Headers. Consulte CORS em APIs Node.js.

Testes de contrato

Para cada versão, teste:

  • schemas de entrada;
  • schemas de saída;
  • status HTTP;
  • headers;
  • autorização;
  • erros;
  • paginação;
  • compatibilidade com exemplos documentados.

Teste de regressão

test('v1 mantém o campo name', async () => {
  const response = await request('/api/v1/users/42');
  assert.equal(typeof response.body.name, 'string');
  assert.equal('profile' in response.body, false);
});

Uma refatoração do domínio não deve alterar o contrato antigo sem intenção.

Consumer-driven contracts

Contratos orientados pelos consumidores ajudam a descobrir expectativas reais. Eles não substituem documentação central, mas verificam que o provedor continua atendendo integrações críticas.

Gateway

Um API gateway pode rotear versões para serviços distintos, aplicar autenticação e coletar métricas. Ainda assim, regras de negócio e serialização precisam permanecer testadas no serviço.

Versões em microsserviços

Não obrigue todos os serviços a mudar ao mesmo tempo. Use adapters, eventos compatíveis e implantações graduais. Uma versão pública pode ser atendida por múltiplas versões internas.

Idempotência

Ao migrar operações de escrita, preserve semântica de idempotência. Clientes podem repetir chamadas durante a transição. Uma nova versão não deve duplicar pagamentos ou pedidos.

Erros comuns

  • Criar versão para toda mudança: a manutenção explode.
  • Alterar v1 silenciosamente: clientes quebram.
  • Copiar toda a aplicação: correções divergem.
  • Não medir uso: versões antigas nunca são removidas.
  • Sem guia de migração: clientes adiam a atualização.
  • Header fora do Vary: caches misturam contratos.
  • Remover sem aviso: integrações param em produção.

Boas práticas

  • Versione apenas mudanças incompatíveis.
  • Prefira uma estratégia simples.
  • Compartilhe o domínio.
  • Separe serializers e schemas.
  • Documente cada versão.
  • Publique guia de migração.
  • Meça adoção.
  • Defina prazo de depreciação.
  • Teste contratos antigos.
  • Proteja caches e autenticação.

Conclusão

O versionamento de API no Node.js oferece um caminho controlado para evoluir contratos sem interromper consumidores. A estratégia mais eficiente é aquela que mantém a versão explícita, reduz duplicação e trata depreciação como um processo mensurável.

Com schemas separados, documentação OpenAPI, métricas por versão, testes de contrato e guias de migração, a equipe consegue introduzir mudanças incompatíveis com previsibilidade. A versão antiga continua estável durante o prazo acordado, enquanto novos clientes adotam o contrato mais atual.

Os 10 Melhores Cursos de Programação de 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