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

Http Slim Laravel Package

juststeveking/http-slim

A lightweight Laravel package for slimming HTTP responses, trimming unnecessary headers and overhead so your app returns leaner, cleaner, faster responses with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Resolve the identity of this package before writing code. The published signals conflict: the description frames it as a Laravel middleware that strips response headers, while v2.1.0 release notes describe a PSR-18/17 client with php-http/discovery autodiscovery and a php-http/common plugin system. Those are different architectures. Five minutes of reading settles it:

composer require juststeveking/http-slim:2.1.0
cat vendor/juststeveking/http-slim/composer.json
ls vendor/juststeveking/http-slim/src

composer.json is authoritative. If php-http/discovery and php-http/client-common are absent, treat every autodiscovery/plugin claim as marketing and read this as a thin HTTP client wrapper. Also check the declared php and laravel/framework constraints — a 2021 package won't declare Laravel 10/11 even if it loads.

Pin the version explicitly. composer require juststeveking/http-slim:2.1.0, not ^2.1. There's no upstream release cadence and no dependents to shake out a bad resolution.

Locate the extension point. The package ships an abstract client to extend. From src/, identify:

  • The FQCN of the base class (examples below assume JustSteveKing\HttpSlim\Client — substitute the real one)
  • Constructor arguments for base URI, default headers, and auth
  • Whether the response exposes $response->status or $response->status() and how json() is returned

First use case: wrap exactly one read-only JSON endpoint. Do not start with the integration that has auth refresh, pagination, and webhooks. Day-one success criteria is one class, one method, one passing test against Http::fake().

namespace App\Clients;

use JustSteveKing\HttpSlim\Client;

final class WeatherClient extends Client
{
    protected string $baseUri = 'https://api.example.com/v1/';

    protected array $headers = [
        'Accept' => 'application/json',
    ];

    public function current(string $city): array
    {
        return $this->get('weather', ['city' => $city])->json();
    }
}

Adapt the property names to whatever the source actually uses — but keep the shape. Once it works, every new integration is "extend, add methods," which is the entire value proposition.

Implementation Patterns

One class per API, injected via an interface. The day-to-day payoff is that controllers and jobs never touch Http:: or the base client directly. Give each client an interface so call sites are swappable and testable without the package:

interface WeatherClientContract
{
    public function current(string $city): WeatherReading;
}

// AppServiceProvider
$this->app->bind(WeatherClientContract::class, WeatherClient::class);

This seam is what makes the package cheap to adopt and cheap to abandon. When you eventually drop the dependency, you delete extends Client and reimplement base-URI/header/auth plumbing. Call sites don't move.

Map the slim response into a DTO immediately. The slim response object is convenient for quick calls but it is not a type. Returning ->json() arrays up the stack recreates the untyped-string problem you adopted the package to escape.

public function current(string $city): WeatherReading
{
    $payload = $this->get('weather', ['city' => $city])->json();

    return WeatherReading::fromArray($payload);
}

Keep the raw response inside the client. Callers get domain objects.

Centralise base URI and secrets in config, not class constants.

protected string $baseUri;

public function __construct(private array $config = [])
{
    $this->baseUri = rtrim(config('services.weather.base_uri'), '/') . '/';
    $this->headers = [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . config('services.weather.token'),
    ];
}

Note the rtrim plus explicit trailing slash. Base-URI joining is the single most common source of 404s in client wrappers, and it compounds when the same class hits staging and production.

Lean on Laravel's HTTP client for cross-cutting behaviour. If the package forwards to Illuminate\Support\Facades\Http (verify in source), retries, timeouts, and middleware belong in the client, not the caller:

public function current(string $city): WeatherReading
{
    $response = $this->get('weather', ['city' => $city]);

    if ($response->status() === 429) {
        // throw a domain exception, or rely on a PendingRequest retry if the wrapper exposes one
    }

    return WeatherReading::fromArray($response->json());
}

Confirm from source whether retry()/timeout() pass through. If not, you can still reach the underlying request by overriding the request-building method — but verify before you promise retry behaviour in a design doc.

Fake at the HTTP layer in tests, not the client. This is the biggest practical win and the rule to enforce in code review. Because the request ends up in Laravel's client, Http::fake() intercepts it, giving you real assertions on base URI, headers, and auth with no mocks:

Http::fake([
    'api.example.com/*' => Http::response(['temp' => 18], 200),
]);

$this->app->make(WeatherClientContract::class)->current('London');

Http::assertSent(fn ($request) =>
    str_starts_with($request->url(), 'https://api.example.com/v1/weather')
    && $request->hasHeader('Authorization', 'Bearer ' . config('services.weather.token'))
);

That closure is your regression test for URL assembly, header merge order, and auth — the three things that silently break on a Laravel upgrade.

Keep a reference implementation in the repo. With 0 dependents and no recent releases, your team is the community. One short doc with the base class plus one worked example is what stops the next developer from reaching for raw Http:: because they weren't sure how the wrapper worked.

Gotchas and Tips

You are the maintainer. Price that in on day one. 7 stars, 0 dependents, last release 2021-08-27. There is no upstream to escalate to and no security advisory cadence for a component handling outbound credentials and headers. Two acceptable postures: pin 2.1.0 with a written replacement deadline, or vendor/fork it (MIT permits this) into app/Support/ and drop the Composer dependency. Do not leave it as an unpinned transitive dep in the request path.

Laravel 10/11 signature drift is the most likely breakage. A 2021 base class was written against an older PendingRequest/Factory. An upgrade can produce a fatal on class load — Declaration of ... must be compatible with ... — discovered at deploy, not in tests. Add a smoke test that instantiates each client and hits one faked endpoint, and run it in CI on the Laravel upgrade PR specifically.

Resolve the autodiscovery question before touching dependencies. If php-http/discovery really is in the tree, Composer 2.2+ blocks on allow-plugins and you get an opaque install failure in CI. If it isn't, skip the PSR-18 inventory entirely. And if autodiscovery is real: pin the resolved client explicitly in a service provider. Autodiscovery can silently resolve a different PSR-18 client than your config intends — a behavioural change with no code change on your side, and one of the worst kinds of bug to diagnose in production.

Header merge order is a trap. When you set a default Authorization header on the class and pass per-request headers, know which wins. Read the source, then write the Http::assertSent test above. The failure mode — auth header silently overwritten — shows up as a 401 in staging and costs an hour every time.

config:cache and container boot. Reading config via config() inside a constructor is fine. Storing a resolved client as a container singleton freezes a stale base URI across deploys. Resolve clients per-request; they're cheap unless you've measured otherwise.

Failed responses may not throw. "Streamlined handling of failed or unexpected responses" is marketing, not a contract. Check whether a 4xx/5xx throws or returns a response object with a status. If it returns, every calling method needs an explicit status check — write that check once in a handle() helper on your base class rather than repeating it per endpoint. Assume nothing until you've read the source.

Don't build on the plugin API. If the php-http/common plugin system exists, treat it as reference design, not a load-bearing extension point. Shipped in a minor release, unproven, zero dependents, dormant since 2021. Extension points in abandoned packages become permanent API surface with no upstream compatibility guarantee.

The two extension points that are actually safe: (1) subclassing the base client and overriding request-building methods, and (2) the interface you define yourself at the call-site boundary. The second is the one that matters — that seam turns "remove the package" into a one-file change instead of a refactor. Everything else in this package's surface area should be treated as internal and unstable.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console