Destino Dev

Blog

Laravel 10: consumir APIs externas com Http Client, retries, fallback e cache

Por John Pires · 19 de março de 2026

Laravel 10: consumir APIs externas com Http Client, retries, fallback e cache

Contexto: quando você vai usar isso

Por que chamar APIs externas em uma aplicação Laravel

Aplicações modernas raramente são isoladas. Serviços de pagamento, provedores de dados, APIs de terceiros e microsserviços internos exigem chamadas HTTP confiáveis. Mesmo um CRUD simples pode depender de uma API externa para validação, geolocalização ou faturamento.

Tratar chamadas HTTP de forma adequada evita latência excessiva, falhas catastróficas e problemas de experiência do usuário. Laravel entrega um Http Client moderno (baseado em Guzzle) que simplifica boas práticas como timeouts, retries e cache.

Quando NÃO usar o Http Client do framework

Se a integração exige conexão persistente, sockets customizados, ou protocolos diferentes de HTTP, o Http Client não é a ferramenta certa. Para scripts de baixo nível ou necessidades específicas de performance, considere bibliotecas especializadas.

Para simples integrações REST/JSON e a maioria dos casos web, o Http Client do Laravel é suficiente e recomendado.

Explicação visual com diagramas mentais

Diagrama mental: fluxo básico de uma chamada HTTP

  [App] --(request)--> [Http Client] --(send)--> [API Externa]
     <--(response)--               <--(200/500)--

Esse desenho simples revela pontos de falha: timeout, erros de rede, respostas 5xx, e latência. Precisamos intervir em cada ponto com timeouts, retries, fallbacks e cache para reduzir impacto.

Diagrama mental: fluxo robusto com retries, cache e fallback

  [App] -- check cache? --> yes --> [Cached Response]
                       |
                       no
                       |
                    [Http Client] --(retry n vezes, timeout)--> [API]
                       |                                     |
                   success                               failure
                       |                                     |
                     cache                              fallback -> [Default Response]
                       |
                    [App]

O objetivo é evitar que falhas na API derrubem a funcionalidade do sistema. Preferimos servir dados levemente desatualizados (cache) do que apresentar erro ao usuário.

Primeiro exemplo simples

Jeito errado: file_get_contents e sem timeout

Muitos iniciantes fazem chamadas HTTP com funções básicas do PHP ou sem configurar timeouts. Isso funciona localmente, mas falha facilmente em produção.

Veja o exemplo errado: ele bloqueia o processo e não tem tratamento de erro.

<?php
// O que faz: busca dados de uma API pública usando file_get_contents (jeito errado)
$url = 'https://api.publicapis.org/entries';
$response = file_get_contents($url);
$data = json_decode($response, true);
var_dump($data);

O que acontece: se a API estiver lenta ou indisponível, o script pode travar ou retornar false, gerando warnings e comportamento imprevisível.

Jeito certo: Http::timeout + throw + tratamento de erro

Use o Http Client do Laravel com timeout, throw() e tratamento de exceções. Assim você evita bloqueios e captura erros de forma explícita.

Exemplo funcional com explicação antes e depois do bloco de código.

<?php
// O que faz: solicita dados com timeout e lança exceção em erro
use Illuminate\Support\Facades\Http;

try {
    $response = Http::timeout(5) // 5 segundos de timeout
                   ->throw()    // lança HttpClientException em 4xx/5xx
                   ->get('https://api.publicapis.org/entries');

    $body = $response->json();
    // use $body com segurança
} catch (\Throwable $e) {
    // fallback: log e resposta padrão
    report($e);
    $body = ['entries' => []];
}

// O que acontece: sua aplicação não fica travada; erros são tratados explicitamente

Segundo exemplo mais realista

Cenário: buscar preço de produto em fornecedor externo

Imagine uma rota que precisa exibir preço atual de um produto chamado sku-123, consultando um fornecedor externo. Queremos:

  • Cachear resultado por 30 segundos
  • Tentar 3 retries em caso de erro transitório
  • Ter fallback local caso tudo falhe

Implementação errada: sem cache, sem retry, sem fallback

Essa implementação é simples, mas cada requisição dependerá do tempo de resposta do fornecedor. Em picos, o site fica lento ou retorna erro.

<?php
// O que faz: busca preço direto na rota (sem cache/retries/fallback)
use Illuminate\Support\Facades\Http;

Route::get('/product/{sku}/price', function ($sku) {
    $response = Http::get('https://supplier.example.com/api/price/' . $sku);
    if ($response->ok()) {
        return $response->json();
    }
    return response()->json(['error' => 'Fornecedor indisponível'], 503);
});

O que acontece: requests falhos propaga erro 503 para o cliente; alto custo em latência direta por cada requisição.

Implementação correta: service class com cache, retry, timeout e fallback

Melhor colocar a lógica em um serviço reutilizável. Usaremos Cache::remember e Http::retry.

<?php
// O que faz: classe de serviço que encapsula a chamada externa com cache e retry
namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;

class SupplierPriceService
{
    public function getPrice(string $sku): array
    {
        $cacheKey = "supplier_price:" . $sku;

        return Cache::remember($cacheKey, 30, function () use ($sku) {
            // retry 3 vezes com 100ms entre tentativas; timeout de 3s
            $response = Http::timeout(3)
                           ->retry(3, 100)
                           ->get("https://supplier.example.com/api/price/{$sku}");

            if ($response->successful()) {
                return $response->json();
            }

            // fallback interno: preço padrão ou lançar exceção
            return ['price' => null, 'source' => 'fallback'];
        });
    }
}

// O que acontece: chamadas são cacheadas por 30s, retries reduzem erros transitórios

Integrar com a rota é trivial: injete SupplierPriceService e retorne o resultado. Esse padrão melhora latência percebida e resiliência do sistema.

Erros comuns de iniciante e como evitar

Erro 1: não usar timeout (bloqueio do processo)

Sem timeout, uma requisição lenta mantém o worker ocupado. Em servidores PHP-FPM isso reduz o throughput e pode causar indisponibilidade. Sempre configure timeout apropriado ao seu caso (2-10s para chamadas de usuário, maior para jobs).

Use Http::timeout(5) para limitar espera. Em loops ou jobs, favor timeouts menores e estratégias de backoff.

Erro 2: retry indiscriminado sem avaliar idempotência

Retry é ótimo para GETs, mas perigoso para endpoints que fazem alterações (POST/PUT) sem garantia de idempotência. Repetir uma operação que cria um recurso pode gerar duplicação.

Regra prática: só usar retry automático para métodos seguros (GET, HEAD) ou quando a API garante idempotência.

Erro 3: não usar cache onde faria sentido

Pergunte: os dados precisam ser sempre em tempo real? Para preços e dados que mudam pouco, cache reduz requisições e custos. Cache curto (20-60s) costuma ser suficiente para UX e reduz pressão na API.

Cache também protege contra falhas: quando a API cair, você pode servir dados levemente obsoletos em vez de erro.

Erro 4: ignorar respostas 4xx/5xx e assumir OK

Verificar apenas conexão não é suficiente. Use $response->successful(), ->ok(), ->clientError(), ->serverError() ou ->throw() para lidar corretamente com cada caso.

Não parseie corpo JSON sem validar; chaves esperadas podem não existir em respostas de erro.

Dicas práticas e convenções

Regra curta: timeout + retry responsável + cache + fallback = clientes menos frustrados e menos incidentes.

Implemente métricas ao redor das chamadas (contadores de erro, latência) e alarmes para detectar degradação de terceiros rapidamente.

Implementações úteis e padrões avançados (extras para praticar)

Uso do rescue para fallback simples

Laravel possui helper rescue() que executa uma callback e retorna um fallback em caso de exceção. Bom para handlers rápidos em controllers.

<?php
// O que faz: tenta buscar e retorna fallback em erro via rescue()
use Illuminate\Support\Facades\Http;

$body = rescue(function () {
    return Http::timeout(2)->get('https://api.example.com/data')->json();
}, function ($e) {
    report($e);
    return ['data' => []];
});

// O que acontece: comportamento conciso com fallback sem try/catch verboso

Fallback avançado: usar cache antigo como fallback

Em vez de retornar dados vazios, use versão antiga no cache. Isso dá a melhor experiência quando a API fica off momentaneamente.

<?php
// O que faz: tenta atualizar cache, mas retorna valor antigo se a atualização falhar
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

function getCachedOrFresh(string $key, callable $fetcher, int $seconds = 30) {
    $old = Cache::get($key);

    try {
        $fresh = $fetcher();
        Cache::put($key, $fresh, $seconds);
        return $fresh;
    } catch (\Throwable $e) {
        report($e);
        return $old ?? ['fallback' => true];
    }
}

// O que acontece: usuários recebem dados antigos ao invés de erro total

Conclusão

Resumo prático

Trabalhar com APIs externas exige disciplina: defina timeouts, escolha retries com responsabilidade, proteja-se com cache e ofereça fallback razoável. O Http Client do Laravel fornece primitives que facilitam adotar essas práticas de forma consistente.

No nível do código, prefira classes de serviço que encapsulam lógica externa, use Cache::remember para reduzir requisições e Http::timeout()->retry()->throw() para tornar falhas previsíveis e tratáveis.

Checklist rápido para revisão de código (code review)

  1. Existe timeout configurado? (sim / não)
  2. Retry é usado apenas em operações idempotentes?
  3. Há cache com TTL apropriado?
  4. Existe fallback/valor padrão em caso de falha?
  5. Erros são logados e métricas expostas?

Seguir essa checklist transforma integrações frágeis em integrações previsíveis. Em code reviews, busque essas quatro práticas básicas: timeout, retry consciente, cache e fallback.

Este artigo apresentou exemplos do jeito errado e do jeito certo, com código real em PHP para Laravel 10. Aplique os padrões em seus controllers, jobs e serviços. Comece pequeno: adicione timeout e cache; depois acrescente retry e fallback conforme necessário.

Próximo passo: transforme um endpoint real do seu projeto em um Service seguindo os exemplos aqui — adicione métricas e monitore impacto antes e depois.

Assuntos: PHP, Laravel, http-client, cache, retries, apis

Leia também