FNSP Web Page

Idempotência: o que acontece quando carregam duas vezes

A ligação está lenta, a pessoa carrega em "Pagar", não acontece nada visível, e carrega outra vez. Ou o telemóvel perde a rede a meio e a aplicação repete o pedido sozinha. Em qualquer dos casos, o servidor recebe dois pedidos iguais — e a pergunta é se isso faz duas encomendas.

1. O que a palavra quer dizer

Uma operação é idempotente quando repeti-la dá o mesmo resultado que fazê-la uma vez. Não é "não faz nada da segunda vez" — é "o estado final é o mesmo".

  • GET, PUT e DELETE são idempotentes por definição do protocolo.

  • POST não é, e é precisamente com ele que se criam coisas.

2. A chave de idempotência

Quem faz o pedido gera um identificador e envia-o. O servidor guarda-o com a resposta:

await fetch('/api/encomendas', {
  method: 'POST',
  headers: { 'Idempotency-Key': chaveGeradaUmaVez },
  body: JSON.stringify(encomenda),
});

const jaFeita = await db.pedidos.findUnique({ where: { chave } });
if (jaFeita) return jaFeita.resposta; // devolve o mesmo, sem repetir nada

const encomenda = await criarEncomenda(dados);
await db.pedidos.create({ data: { chave, resposta: encomenda } });
return encomenda;

O detalhe que faz isto funcionar: a chave é gerada uma vez, quando o formulário abre — e não a cada tentativa. Se for gerada a cada envio, as duas tentativas têm chaves diferentes e voltamos ao princípio.

3. A base como última defesa

Onde houver um campo naturalmente único, o índice único resolve sem lógica nenhuma:

CREATE UNIQUE INDEX ON votos (artigo_id, votante);

O segundo voto não passa, e passa a ser um erro tratável em vez de uma linha duplicada. É o mesmo princípio das chaves de idempotência, escrito no esquema.

4. E do lado do botão

// Desativar enquanto o pedido está a decorrer resolve o caso comum
<button disabled={aEnviar}>{aEnviar ? 'A enviar…' : 'Pagar'}</button>

Resolve o caso comum, e não resolve o resto: uma repetição automática da rede, um clique num separador duplicado, um robô. A defesa do lado do cliente é cortesia; a do servidor é que é a defesa.

Comentários

Ainda ninguém comentou este artigo.

Voltar ao blog

Gostávamos de saber quantas pessoas visitam o site, com o Google Analytics. Sem a sua autorização não corre nada. Política de Privacidade.