Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Guzzle Laravel Package

astrotomic/laravel-guzzle

A lightweight Laravel integration for the Guzzle HTTP client. Configure Guzzle clients through Laravel's config and service container, resolve them via dependency injection, and make outbound HTTP requests with a familiar, framework-native setup.

View on GitHub
Deep Wiki
Context7

Getting Started

This release (2.4.0) is a maintenance release: PHP 8.1 support was added, illuminate/support was bumped, and the rest is CI/dependency hygiene. There are no breaking changes and no new public API, so an existing 2.3.x integration keeps working — but if you are on Laravel 9 / PHP 8.1, ^2.4 is the constraint you want.

composer require astrotomic/laravel-guzzle:^2.4
php artisan vendor:publish --provider="Astrotomic\LaravelGuzzle\GuzzleServiceProvider" --tag=config

First things to look at, in order:

  1. config/guzzle.php — the package's whole value proposition is that your Guzzle client is configured from config instead of being hand-built in every service. Read the keys before writing any HTTP code; this is the file you will actually edit day-to-day.
  2. The Guzzle facade (aliased in the package's service provider) — your entry point for ad-hoc calls.
  3. The container binding for GuzzleHttp\Client — this is what makes the package composable with your own classes via constructor injection.

First use case: replace a hand-rolled new Client([...]) somewhere in your app with config + injection, then confirm the base URI and timeout come from the environment.

// config/guzzle.php — set from env, not hardcoded
'base_uri' => env('API_BASE_URI'),
'timeout'  => env('API_TIMEOUT', 10),
'headers'  => ['Accept' => 'application/json'],
use GuzzleHttp\Client;
use Astrotomic\LaravelGuzzle\Facades\Guzzle;

// via container (preferred — swappable in tests)
$client = app(Client::class);

// via facade (fine for tinker, Artisan commands, one-off scripts)
$response = Guzzle::get('users');

Implementation Patterns

Constructor injection for anything testable. Because the client is a container singleton, injecting GuzzleHttp\Client is the integration point. Bind a fake or point base_uri at a local stub in your test suite rather than mocking the facade.

final class UserApi
{
    public function __construct(private Client $http) {}

    public function find(int $id): array
    {
        return json_decode(
            $this->http->get("users/{$id}")->getBody()->getContents(),
            true,
            512,
            JSON_THROW_ON_ERROR
        );
    }
}

Don't re-new Client() inside services. The moment a second Guzzle client is constructed by hand, you've got two timeout/header policies and the config file becomes a lie. If you genuinely need a different upstream, keep it explicit and named — bind a second client under its own key in a service provider that reads a different config array, and inject Client $githubClient with a #[Target]-style alias or a small factory.

Wrap the client, don't leak it. The most common durable pattern with this package is a thin per-API adapter class (as above) that owns URL building, decoding, and error mapping. The package gives you config; your adapter gives you a domain API, and the rest of the app never sees Guzzle types.

Pair with retry/middleware in config, not in call sites. Guzzle's HandlerStack / retry middleware belongs in the config array (or a small service provider decorating the singleton) so every call benefits. Per-call ['timeout' => 60] overrides sprinkled through controllers are the usual rot vector.

Artisan commands and health checks. The facade is convenient for php artisan code where container injection is awkward; Guzzle::request('HEAD', '/') in a health:check command is a legitimate use.

Test strategy. Two layers: (a) pure unit tests of your adapter with a mocked Client; (b) one or two integration tests against Http::fake()-style stubs or a local server where you assert the config actually reaches the client (base URI, auth header, timeout). The config plumbing is the package's risk surface, so test it once explicitly.

Gotchas and Tips

  • Upgrade path is trivial but pin deliberately. 2.4.0 only widens PHP support and bumps illuminate/support. If you're on PHP 8.0 or older, 2.3.x is still fine — don't rush; if you're on PHP 8.1, 2.4 is the first release that officially supports you.
  • base_uri vs relative paths. Guzzle's base_uri resolution is easy to get wrong: a base URI without a trailing slash plus a leading-slash path discards any path component of the base. If you set https://api.example.com/v1, call users (no leading slash) or make the base URI end in /.
  • Config is read once. The client is typically a singleton, so config() changes at runtime (e.g. Config::set() in a test) won't affect an already-resolved client. Reset the container binding in tests or rebuild the app.
  • timeout is not connect_timeout. The former doesn't cover DNS/TCP setup. For flaky networks set both; otherwise you'll see hangs well past your configured timeout.
  • Empty arrays vs missing keys. 'headers' => [] and no headers key can behave differently once middleware is involved. Be explicit in config rather than relying on merge semantics.
  • Exceptions, not status codes. Guzzle throws RequestException/ConnectException by default. If you don't catch them, an upstream 500 becomes a 500 in your own app with a confusing stack trace. Decide once — http_errors => false and check status, or catch and map to a domain exception.
  • Debugging: 'debug' => true in config dumps request/response to stdout — invaluable locally, catastrophic in production logs (auth headers included). Gate it on env('APP_DEBUG') and consider Guzzle's Middleware::history() for a structured record instead.
  • Extension point: the singleton binding is the seam. Decorate it in your own service provider ($this->app->extend(Client::class, ...)) to add logging, tracing, or a retry stack without forking the package or editing vendor config.
  • composer normalize / CI churn in this release is not a signal. The bulk of 2.4.0's diff is Dependabot and GitHub Actions bumps. The package is small and stable; treat it as configuration glue, not as something that will grow features, and don't expect new methods between minor versions.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core