dragon-code/laravel-http-macros
dragon-code/laravel-http-macros adds handy macros to Laravel's HTTP client, cutting boilerplate for common API request tasks such as headers, authentication, timeouts, retries, and JSON payloads. Build cleaner, more expressive HTTP calls with less repetitive code.
Install with Composer; the package is auto-discovered, so no manual provider registration is needed:
composer require dragon-code/laravel-http-macros
First place to look is the package's service provider — that's the canonical list of what you actually get. The whole package is essentially a boot() method calling Http::macro('name', fn () => ...) on Illuminate\Http\Client\PendingRequest, so vendor/dragon-code/laravel-http-macros/src/ is small enough to read end-to-end in five minutes. Do that before writing any code; the README is thinner than the source and the macro list is the API surface.
Check the installed version against your framework version before you start. The 1.5.0 release added Laravel 13 compatibility, so this package now tracks Laravel majors in lockstep — read composer.json's illuminate/* constraint rather than assuming the version you get is the version your app needs. On Laravel 13 you need 1.5.0 or later; on older majors, anything from the last few releases will do.
Your first use case will almost certainly be collapsing repeated request boilerplate. Anywhere you currently write the same three or four chained calls before every ->get(), the macro is a one-liner:
// before
Http::withHeaders(['X-Api-Version' => '2'])
->withOptions(['connect_timeout' => 5])
->acceptJson()
->get($url);
// after
Http::yourMacroName()->get($url);
Because Factory::__call() forwards unknown methods to a fresh PendingRequest, the macro is reachable both statically off the facade (Http::yourMacroName()) and mid-chain on an existing request object — either form returns the pending request so you can keep chaining.
Chain it like a native method. Macros are ordinary PendingRequest methods, so ordering relative to native calls matters the same way it does for withHeaders()/withToken(). Put the macro where its effect belongs: authentication-style macros before the terminal verb, per-call overrides after it.
Wrap, don't scatter. The highest-leverage pattern is a thin domain client that composes the macro with your own defaults, so feature code never touches the HTTP layer:
final class BillingClient
{
public function charges(int $page): Response
{
return Http::billingAuth()
->baseUrl(config('services.billing.url'))
->retry(3, 200)
->get('/charges', ['page' => $page]);
}
}
Compose with your own macros. Register app-specific macros in AppServiceProvider::boot() alongside the package's. Guard with PendingRequest::hasMacro('name') if you want to be defensive about future package or framework changes:
if (! PendingRequest::hasMacro('internalApi')) {
PendingRequest::macro('internalApi', fn () => $this->withToken(config('services.internal.token')));
}
Keep it inside Http::fake(). Macros mutate the pending request, not the transport, so Http::fake() / Http::assertSent() still see the fully assembled request. Assert on the outgoing Request to prove the macro did what you think:
Http::fake();
app(BillingClient::class)->charges(1);
Http::assertSent(fn (Request $r) => $r->hasHeader('Authorization'));
Use beforeSending/global middleware for cross-cutting concerns, not macros. Macros are configuration sugar; anything that needs to observe or rewrite every request (correlation IDs, logging) belongs in Http::globalRequestMiddleware() so it applies even to requests that forgot the macro.
Maintenance is real but thin. Earlier versions of this package were flagged as effectively unmaintained; 1.5.0 walks that back with Laravel 13 support, which is a genuine signal that the package is being kept alive across framework majors. That said, the delta in 1.5.0 is one compatibility commit plus four asset/README/licence chores from the bot — no new macros, no bug fixes, no tests. So treat it as a thin, low-velocity dependency: safe to install, cheap to vendor if it ever stalls. If you only need one or two macros, copying them into your own service provider still costs nothing and removes the upgrade coupling.
Pin your constraint if you're on a new Laravel major. Because compatibility is added per release rather than via a permissive illuminate/support range, a ^1.4 lock is what silently keeps you off Laravel 13. Use ^1.5 on L13 and check the changelog before bumping the framework, not after composer update fails.
Macro names are global and unnamespaced. Collisions are silent — whoever registers last wins, and Laravel native methods always beat macros because __call only fires for undefined methods. If the package's macro shares a name with a method Laravel later adds natively, your macro quietly stops running after an upgrade. This is the class of failure that a "Laravel 13 compatibility" release can cause even while fixing the framework constraint: a new native PendingRequest method can shadow a macro name that used to work. Names like withHeaders are fine; a generic ->auth() is a landmine.
No IDE support out of the box. There are no stubs, so PhpStorm won't autocomplete anything. Add @method annotations to a docblock on your own facade or client wrapper, or the macros become write-only code that new team members can't discover.
State lives on the request instance. Http::macro() / Http::anyMacro() creates a new PendingRequest each time, so you can't call a macro statically and reuse the object across pool entries or retries expecting shared state. Http::pool() callbacks receive their own instances — the macros are available there, but configuration doesn't leak between them.
Debugging is the same as native. ->dump() / ->dd() on the pending request shows the final assembled request; if a macro isn't firing, confirm with PendingRequest::hasMacro('name') and check that nothing earlier in the chain reset the options array. Most "the macro did nothing" bugs are an ordering issue where a later withOptions() overwrote what the macro set, or — post-upgrade — a native method that now exists and shadows the macro.
Extension points worth knowing: PendingRequest::macro(), Response::macro() for response-side helpers (->assertApiError() style), and Http::globalRequestMiddleware() for anything mandatory. Response macros in particular are often more valuable than request macros and cost nothing to maintain yourself.
How can I help you explore Laravel packages today?