A API deles caiu. Por que o SEU app caiu junto?
O fornecedor de frete teve um problema. A API deles ficou lenta — respondendo em 40 segundos em vez de 200 milissegundos.
Do lado deles, um incidente. Do seu lado? Caos.
Cada requisição que seu app fez pra calcular o frete ficou pendurada esperando. Os workers da fila entupiram. As conexões do PHP-FPM esgotaram. Em poucos minutos, seu app inteiro parou de responder — não só o checkout, tudo.
Um problema que era deles virou seu problema. E o pior: era evitável com três linhas.
O código que confia demais no mundo lá fora
Chamada de API é um ato de fé. Você pede algo pra um servidor que não é seu e espera que ele responda rápido, sempre, sem erro. Spoiler: ele não vai.
// Confiança cega: espera pra sempre, e reza pra dar certo
$response = Http::get('https://api-frete.com/cotacao', [
'cep' => $cep,
]);
return $response->json();
O que tá faltando aqui?
- Sem limite de espera. Se a API travar, sua requisição trava junto. Por padrão o HTTP Client espera até 30 segundos — uma eternidade quando isso acontece em cascata.
- Sem tentar de novo. Uma falha de rede momentânea (acontece o tempo todo) já derruba a operação inteira.
-
Sem tratar erro. Se a API devolver 500, o
$response->json()segue como se nada fosse, e você processa lixo achando que é cotação.
A blindagem: timeout, retry e throw
O HTTP Client do Laravel já traz o resgate embutido. Você encadeia e pronto:
$response = Http::timeout(5) // desiste depois de 5s, não fica pendurado
->retry(3, 200) // tenta 3x, esperando 200ms entre as tentativas
->get('https://api-frete.com/cotacao', ['cep' => $cep]);
$response->throw(); // erro 4xx/5xx? lança exceção, não segue com lixo
return $response->json();
Três métodos, três problemas resolvidos:
-
timeout(5)— se em 5 segundos não respondeu, o Laravel desiste e lança umaConnectionException. Sua requisição não fica travada arrastando o app junto. -
retry(3, 200)— falha de rede? Ele tenta de novo, até 3 vezes, com 200ms de intervalo. A instabilidade passageira some sozinha. -
throw()— se depois de tudo a resposta for erro, ele lança exceção. Você trata de propósito, em vez de processar dado inválido silenciosamente.
Como usar na prática
Alguns ajustes que valem a pena conhecer:
// Separar timeout de conexão do timeout de resposta
Http::connectTimeout(3)->timeout(10)->get(/* ... */);
// Só relançar em erro de servidor, não em 404 esperado
$response->throwIf($response->serverError());
// Ou garantir que veio exatamente 200
$response->throwUnlessStatus(200);
connectTimeout é quanto você espera pra conectar (3s costuma bastar). timeout é o total até a resposta chegar. Separar os dois te dá controle fino: "conecta rápido, mas se conectou, dou mais um tempinho pra processar".
A pegadinha: retry cego pode cobrar o cartão duas vezes
Aqui mora o perigo que quase ninguém pensa. O retry é maravilhoso pra operações idempotentes — buscar uma cotação, consultar um CEP. Repetir não faz mal.
Mas e uma cobrança? Se a API processou o pagamento e a resposta se perdeu no caminho, o retry vai tentar de novo — e cobrar o cliente duas vezes. 😬
Pra esses casos, o retry aceita uma condição: só repete se for erro de conexão (a requisição nem chegou), nunca se o servidor já respondeu:
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\PendingRequest;
Http::retry(3, 200, function ($exception, PendingRequest $request) {
// só tenta de novo se NEM CHEGOU no servidor
return $exception instanceof ConnectionException;
})->post('https://gateway.com/charges', $dados);
A régua: repetir consulta, sempre. Repetir cobrança, só se tiver certeza de que nem chegou.
Bônus: e agora?
Trocar Guzzle na mão pelo Http:: do Laravel não é só sintaxe mais bonita. É timeout, retry, throw e — de brinde — a possibilidade de mockar tudo nos testes com Http::fake(), sem tocar na API real (isso rende um post inteiro à parte).
Se você tem uma integração externa crítica sem timeout, essa é sua lição de casa pra hoje. Uma API lenta lá fora não pode ser capaz de derrubar seu app aqui dentro.
Me conta: seu app já foi derrubado por uma API de terceiro? O que travou primeiro — a fila ou o FPM? 👀
Top comments (0)