Pular para o conteúdo

Idempotência em APIs: O que é, como funciona e como evitar requisições duplicadas

Imagine a seguinte situação: Você acabou de realizar um pagamento pelo aplicativo do banco. Após confirmar a operação, sua conexão com a internet cai antes da resposta aparecer na tela. Sem saber se o pagamento foi realmente processado, você faz o que praticamente qualquer pessoa faria: tenta novamente.

Agora imagine que o sistema processe as duas requisições.

  • Dois pagamentos.
  • Duas cobranças no cartão.
  • Dois pedidos registrados.

Esse é um problema muito mais comum do que parece. Em sistemas distribuídos, falhas de rede, timeouts, perda de conexão e mecanismos automáticos de Retry fazem com que uma mesma requisição possa ser enviada diversas vezes.

A pergunta é: como a API sabe que aquela operação já foi executada?

É justamente esse problema que a Idempotência procura resolver.

O que é Idempotência?

Idempotência é a propriedade que permite executar uma mesma operação diversas vezes sem alterar o resultado obtido após a primeira execução.

Em outras palavras, mesmo que uma requisição seja reenviada várias vezes, o servidor deverá produzir exatamente o mesmo efeito da primeira execução.

Isso não significa que a API ignore a nova requisição. Ela apenas identifica que aquela operação já foi processada anteriormente e devolve o mesmo resultado ao cliente, evitando efeitos colaterais como registros duplicados.

Por que esse problema acontece?

Muitas pessoas imaginam que uma requisição HTTP é enviada apenas uma vez. Na prática, isso raramente acontece em ambientes de produção.

Diversos fatores podem fazer uma mesma operação ser reenviada automaticamente.

  • Timeout da requisição.
  • Queda temporária da conexão.
  • Retry automático do cliente.
  • Retry realizado pelo API Gateway.
  • Falhas temporárias na rede.
  • Balanceadores de carga.

Em todos esses cenários, o cliente normalmente não sabe se a primeira requisição foi concluída com sucesso. Como consequência, ele simplesmente envia a mesma requisição novamente.

O problema das requisições duplicadas

Vamos imaginar uma API responsável por criar pagamentos.

POST /payments
Host: api.example.com
Content-Type: application/json

{
  "customerId": 100,
  "amount": 350.00
}

A API recebe a requisição, processa o pagamento e prepara a resposta. Entretanto, exatamente nesse momento ocorre uma falha de rede. O pagamento foi criado, mas o cliente nunca recebeu a confirmação. Sem saber disso, ele envia novamente exatamente a mesma requisição. Se a API não possuir nenhum mecanismo de proteção, ela criará um segundo pagamento.

Fluxo sem Idempotência

Esse tipo de situação não acontece apenas em APIs de pagamento.

  • Pedidos duplicados em e-commerce.
  • Usuários cadastrados duas vezes.
  • Envio duplicado de e-mails.
  • Emissão repetida de notas fiscais.
  • Criação duplicada de ordens de produção.

Perceba que o problema não está na rede nem no usuário. O verdadeiro desafio é permitir que uma operação seja repetida com segurança, mesmo quando ocorrerem falhas durante a comunicação.

Mas como uma API consegue identificar que uma requisição já foi executada anteriormente?

É exatamente aqui que entra um dos mecanismos mais utilizados atualmente: a Idempotency-Key.

Como a API sabe que aquela requisição já foi processada?

A solução mais utilizada atualmente é a Idempotency-Key.

Ela funciona como um identificador único enviado pelo cliente juntamente com a requisição. Sempre que uma operação precisar ser executada apenas uma vez, o cliente gera uma chave exclusiva e a envia no cabeçalho HTTP.

Se essa mesma requisição precisar ser reenviada devido a um timeout ou falha de comunicação, a mesma chave também será enviada.

Assim, a API consegue identificar que aquela operação já foi executada anteriormente.

O que é uma Idempotency-Key?

Idempotency-Key é um identificador único utilizado para representar uma operação específica. Normalmente essa chave é um UUID gerado pelo cliente antes do envio da requisição.

O servidor utiliza essa chave para verificar se aquela operação já foi processada anteriormente. Caso a resposta já exista, ela é retornada novamente sem executar a operação outra vez.

POST /payments
Host: api.exemplo.com
Idempotency-Key: 9f7d31d9-6909-49fa-94c3-38d6e7df32d8
Content-Type: application/json

{
  "customerId": 100,
  "amount": 350.00
}

Após receber essa requisição, a API consulta sua base de dados para verificar se essa chave já foi utilizada.

Fluxo com Idempotência

Observe que, na segunda tentativa, a API não executa novamente a lógica de negócio. Ela apenas devolve a resposta registrada durante a primeira execução.

Exemplo Prático

Vamos imaginar novamente o cenário do pagamento.

Primeira tentativa:

POST /payments
Idempotency-Key: 8f73e0f1
Content-Type: application/json

{
  "customerId": 100,
  "amount": 350.00
}

A API cria o pagamento.

Status: HTTP/1.1 201 Created
Content-Type: application/json

{
  "paymentId": 451,
  "status": "APPROVED"
}

Agora imagine que o cliente não recebeu essa resposta por causa de um timeout.

Ele envia exatamente a mesma requisição novamente.

POST /payments
Idempotency-Key: 8f73e0f1
Content-Type: application/json

{
  "customerId": 100,
  "amount": 350.00
}

Dessa vez, a API identifica que a chave já existe.

Em vez de criar um novo pagamento, ela retorna exatamente a mesma resposta da primeira execução.

Status: HTTP/1.1 201 Created
Content-Type: application/json

{
  "paymentId": 451,
  "status": "APPROVED"
}

Perceba que o cliente recebe a confirmação normalmente, mas nenhum novo registro é criado no banco de dados.

Todos os métodos HTTP são idempotentes?

Não.

Alguns métodos já são considerados naturalmente idempotentes pela própria especificação HTTP. Outros dependem da forma como a aplicação foi implementada.

Método HTTPIdempotente?Descrição
GET✔ SimApenas consulta dados.
HEAD✔ SimRetorna apenas os cabeçalhos.
OPTIONS✔ SimConsulta recursos disponíveis.
PUT✔ SimSubstitui completamente um recurso.
DELETE✔ SimExcluir novamente não altera o resultado final.
PATCH⚠ DependeDepende da implementação.
POST❌ NãoNormalmente cria novos recursos.

É justamente por isso que APIs de pagamento, bancos, marketplaces e plataformas de assinatura normalmente implementam Idempotency-Key para operações realizadas através do método POST.

Idempotência em Microsserviços

Até aqui utilizamos uma API REST como exemplo, mas a idempotência vai muito além disso.

Em arquiteturas baseadas em Microsserviços, uma única ação do usuário pode gerar diversos eventos e acionar vários serviços independentes.

Imagine que um cliente finalize uma compra.

Cada serviço consome o mesmo evento de forma independente.

Mas existe um detalhe importante.

Nem sempre uma mensagem é entregue apenas uma vez.

Dependendo da tecnologia utilizada, um consumidor pode receber exatamente o mesmo evento duas ou mais vezes.

Se o serviço não for idempotente, diversos problemas podem acontecer.

  • O estoque pode ser reduzido duas vezes.
  • O cliente pode receber dois e-mails.
  • Duas notas fiscais podem ser emitidas.
  • O pagamento pode ser processado novamente.

Por esse motivo, consumidores de eventos também precisam ser idempotentes.

Idempotência em Mensageria

Em sistemas que utilizam mensageria, o objetivo principal não é impedir que uma mensagem seja entregue novamente.

O objetivo é garantir que ela possa ser processada várias vezes sem alterar o resultado final.

Esse conceito é extremamente importante em plataformas como Apache Kafka e RabbitMQ.

Apache Kafka

No Kafka, um consumidor pode reprocessar mensagens após falhas, reinicializações ou mudanças de partições.

Isso significa que um mesmo evento pode chegar novamente ao consumidor.

Se a aplicação não for idempotente, o mesmo processamento poderá acontecer diversas vezes.

RabbitMQ

No RabbitMQ acontece algo semelhante.

Se um consumidor falhar antes de enviar o ACK da mensagem, ela poderá voltar para a fila e ser entregue novamente.

Por isso, o consumidor deve conseguir identificar que aquela mensagem já foi processada anteriormente.

Como implementar Idempotência

Não existe uma única forma de implementar esse mecanismo.

Entretanto, a estratégia mais comum consiste em armazenar um identificador único juntamente com o resultado da operação.

IdempotencyRequests
-----------------------------------------
Id
IdempotencyKey
StatusCode
Response
CreatedAt
ExpiresAt
-----------------------------------------

Quando uma nova requisição chega, a API consulta essa tabela antes de executar qualquer regra de negócio.

Se a chave existir, a resposta armazenada é devolvida imediatamente.

Caso contrário, a operação é executada normalmente e o resultado é salvo para futuras tentativas.

Boas Práticas

  • Utilize UUID como Idempotency-Key.
  • Defina um tempo de expiração para as chaves.
  • Armazene também a resposta retornada ao cliente.
  • Garanta que a gravação da chave e da operação seja atômica.
  • Implemente logs para facilitar auditorias.
  • Não reutilize uma mesma chave para operações diferentes.

Erros Comuns

  • Confiar apenas no método HTTP.
  • Executar a operação antes de verificar a Idempotency-Key.
  • Armazenar as chaves indefinidamente.
  • Ignorar processamento duplicado em consumidores de eventos.
  • Não validar se a mesma chave está sendo utilizada com dados diferentes.

Conclusão

Falhas de comunicação fazem parte da realidade de qualquer sistema distribuído. Timeouts, perda de conexão e mecanismos automáticos de retry acontecem diariamente e não podem ser totalmente evitados.

Por esse motivo, APIs modernas precisam ser capazes de reconhecer quando uma mesma operação já foi executada anteriormente.

A Idempotência resolve exatamente esse problema, permitindo que uma mesma requisição seja enviada diversas vezes sem produzir efeitos colaterais adicionais.

Mais do que uma característica de APIs REST, ela é um dos pilares para construir aplicações resilientes baseadas em microsserviços, mensageria e arquiteturas orientadas a eventos.

Hercílio Simões

Hercílio Simões

Software Engineer, formado em Ciência da Computação e Redes de Computadores e certificado AWS Cloud Practitioner. Atuou na DGAE, órgão do Ministério da Educação de Portugal, e atualmente trabalha na Targa Telematics, multinacional europeia especializada em soluções de mobilidade conectada.

Neste blog compartilha conteúdos sobre engenharia de software, arquitetura de software, cloud computing, AWS e sistemas distribuídos.