Como Projetar APIs REST Idempotentes e Versionadas
Você faz o deploy de uma nova funcionalidade e, de repente, sua API recebe requisições POST duplicadas de clientes que tentam novamente após um timeout. Seu banco de dados agora tem dois pedidos idênticos. Ou você lança uma mudança que quebra a compatibilidade, e os aplicativos móveis travam porque esperavam o formato de resposta antigo. Esses são dois dos problemas mais comuns e dolorosos no design de APIs: operações não idempotentes e mudanças que quebram compatibilidade. Este guia mostra como resolver ambos com padrões práticos de idempotência e versionamento em APIs REST.
Por que a Idempotência Importa em APIs REST
Idempotência significa que fazer a mesma requisição várias vezes tem o mesmo efeito que fazê-la uma vez. Os métodos HTTP têm semânticas de idempotência definidas: GET, HEAD, PUT, DELETE e OPTIONS são idempotentes, enquanto POST e PATCH não são. Mas APIs do mundo real frequentemente precisam aceitar POST para operações como criar recursos ou processar pagamentos. Falhas de rede, timeouts de clientes e lógica de retry podem causar duplicatas. Sem idempotência, você corre o risco de cobranças duplicadas, registros duplicados ou estado inconsistente.
Técnicas para APIs REST Idempotentes
1. Use Idempotency Keys para Requisições POST
A abordagem mais comum é permitir que os clientes enviem uma chave única (por exemplo, um UUID) em um cabeçalho Idempotency-Key. O servidor armazena a chave e o resultado da operação. Se a mesma chave for recebida novamente, o servidor retorna o resultado armazenado em vez de reexecutar a operação.
POST /payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"amount": 1000,
"currency": "USD"
}
Etapas de implementação:
- O cliente gera uma chave única (UUID v4) para cada operação lógica.
- O servidor verifica se a chave existe em um armazenamento persistente (por exemplo, Redis ou banco de dados).
- Se não existir, processa a requisição e armazena a chave com o status e o corpo da resposta.
- Se existir, retorna a resposta armazenada sem reprocessar.
Defina uma expiração razoável (por exemplo, 24 horas) para evitar crescimento ilimitado do armazenamento.
2. Aproveite Requisições Condicionais
Para atualizações, use os cabeçalhos ETag e If-Match para evitar atualizações perdidas. O servidor retorna um ETag representando o estado atual. O cliente o envia de volta com If-Match; se o recurso mudou, o servidor retorna 412 Precondition Failed. Isso torna as requisições PUT seguras contra condições de corrida.
PUT /articles/123
If-Match: "abc123"
Content-Type: application/json
{"title": "Updated title"}
3. Projete PUT para Idempotência Natural
Prefira PUT em vez de POST quando o cliente pode determinar a URL do recurso. Por exemplo, PUT /users/{userId} é naturalmente idempotente porque chamadas repetidas sobrescrevem o mesmo recurso. Use POST apenas quando o servidor atribui o ID.
4. Trate Detecção de Duplicatas com Restrições Únicas
Combine idempotency keys com restrições únicas de banco de dados em chaves de negócio (por exemplo, número do pedido). Se uma duplicata passar, o banco de dados a rejeita, e você pode retornar um 409 Conflict com uma mensagem clara.
Estratégias de Versionamento para APIs REST
As APIs evoluem. Novos campos, comportamento alterado ou endpoints removidos podem quebrar clientes existentes. O versionamento permite introduzir mudanças sem interrompê-los. Existem várias estratégias comuns:
| Estratégia | Exemplo | Prós | Contras |
|---|---|---|---|
| Caminho da URI | /v1/users |
Explícito, fácil de rotear, compatível com cache | URLs mudam, pode poluir |
| Parâmetro de Consulta | /users?version=1 |
Simples, opcional | Fácil de omitir, não é RESTful |
| Cabeçalho Personalizado | Accept-Version: v1 |
Mantém as URLs limpas | Mais difícil de testar no navegador |
| Media Type | Accept: application/vnd.api.v1+json |
Negociação de conteúdo, REST puro | Complexo, menos comum |
Para a maioria das equipes, o versionamento por caminho da URI é a escolha mais pragmática. É visível, fácil de documentar e funciona bem com gateways de API e CDNs.
Como Versionar Sem Quebrar Clientes
- Comece com v1 no caminho desde o primeiro dia. Adicionar versionamento depois é mais difícil.
- Apenas mudanças aditivas dentro de uma versão: novos campos opcionais, novos endpoints. Nunca remova ou renomeie campos.
- Descontinue gradualmente: anuncie o fim da vida útil, forneça guias de migração e monitore o uso de versões antigas.
- Use cabeçalhos sunset:
Sunset: Sat, 31 Dec 2025 23:59:59 GMTpara informar os clientes. - Mantenha no máximo duas versões ativas para limitar a carga de manutenção.
Juntando Tudo: Um Exemplo Prático
Considere uma API de pagamentos. Você quer criar um pagamento de forma idempotente e versionar o endpoint.
POST /v1/payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{
"amount": 1000,
"currency": "USD"
}
Lógica do servidor:
- Verifique se
Idempotency-Keyexiste no Redis. - Se sim, retorne a resposta em cache (status + corpo).
- Se não, processe o pagamento, armazene a chave com a resposta, retorne 201 Created.
Para atualizações, use PUT com ETag:
PUT /v1/payments/123
If-Match: "xyz789"
Content-Type: application/json
{"amount": 1500}
Se o ETag não corresponder, retorne 412. Isso evita sobrescrever mudanças concorrentes.
Armadilhas Comuns e Como Evitá-las
- Armazenar idempotency keys para sempre: defina um TTL (por exemplo, 24h) para evitar acúmulo no armazenamento.
- Ignorar condições de corrida: use operações atômicas (por exemplo, Redis SETNX) para verificar e definir chaves.
- Versionar tarde demais: adicione /v1 desde o início.
- Mudanças que quebram compatibilidade em versões menores: trate qualquer mudança que altere a estrutura da resposta como major.
- Não documentar a idempotência: declare claramente quais endpoints suportam Idempotency-Key.
FAQ
O que é uma API REST idempotente?
Uma API REST idempotente garante que fazer a mesma requisição várias vezes produz o mesmo resultado que fazê-la uma vez. Isso é crucial para lidar com retries com segurança, especialmente para métodos não idempotentes como POST.
Quais métodos HTTP são idempotentes?
GET, HEAD, PUT, DELETE, OPTIONS e TRACE são idempotentes. POST e PATCH não são idempotentes por padrão, mas você pode torná-los idempotentes usando técnicas como idempotency keys.
Qual é a melhor forma de versionar uma API REST?
O versionamento por caminho da URI (por exemplo, /v1/users) é a abordagem mais comum e prática. É explícito, fácil de rotear e funciona bem com cache e gateways de API. Outras opções incluem parâmetros de consulta, cabeçalhos personalizados e media types, mas têm trade-offs.
Pronto para testar as respostas JSON da sua API? Use nosso JSON Formatter para validar e formatar payloads rapidamente.