saritasa/laravel-middleware
A Laravel middleware package from Saritasa providing reusable, composable HTTP middleware for request filtering, response handling, and route-level concerns, helping keep controllers clean and application logic organized.
composer require saritasa/laravel-middleware. As of 2.1.3 (2024-08-02) the package explicitly declares Laravel 11 compatibility, so the previous advice to reach for --ignore-platform-reqs is no longer needed on Laravel 11 — a plain install should satisfy illuminate/* constraints.--ignore-platform-reqs in CI or a lockfile workaround from an earlier 2.1.x, drop it and re-resolve (composer update saritasa/laravel-middleware --with-dependencies) so Composer validates the new constraint instead of silently trusting your override.src/ — the README is thin, so the class list is the real documentation. Read each handle() method (they're short) to see what it does, whether it touches the response, and whether it expects config.ServiceProvider is auto-discovered (extra.laravel.providers). If there is one, it usually only publishes config or registers aliases; most middleware still has to be wired by hand.api group, e.g. in bootstrap/app.php:->withMiddleware(function (Middleware $middleware) {
$middleware->api(prepend: [
\Saritasa\Middleware\RequestId::class,
]);
$middleware->alias([
'force.json' => \Saritasa\Middleware\ForceJsonResponse::class,
]);
})
composer require on 2.1.3 proves the constraint resolves, not that the middleware behaves under Laravel 11's new bootstrap.Classify the middleware before registering it. Saritasa packages tend to ship three flavours, and each has a different home:
| Flavour | Example behaviour | Where it goes |
|---|---|---|
| Request prep | request ID, locale from Accept-Language, force JSON |
$middleware->api(prepend: [...]) or global |
| Response decoration | security headers, Content-Length, casing |
append to group, or run last |
| Cross-cutting | request logging, timing | global + terminable |
Global vs. group vs. route alias. Keep request-shaping middleware in the api group so web sessions and CSRF aren't affected. Register as an alias when it's opt-in per route (->middleware('force.json')), which is easier to review in route:list and easier to A/B in a feature flag.
Ordering is the design decision. In Laravel 11+, use prepend/append on the group and understand the priority list:
$middleware->prependToGroup('api', RequestId::class); // runs before auth
$middleware->appendToGroup('api', AddCorrelationHeader::class); // sees final response
Anything that measures latency or logs must be outermost (prepend) so it sees early failures; anything that mutates the finished response must be innermost-appended so it runs after the controller.
Composition over configuration. These middleware are small. The strongest day-to-day pattern is wrapping rather than configuring:
class LoggedForceJson extends ForceJsonResponse
{
public function handle($request, Closure $next)
{
return parent::handle($request, fn ($r) => $next($r->withHeader('X-Api', '1')));
}
}
Upgrading within 2.1.x is a no-op by design. 2.1.3 is a constraint/declaration release, not a code release — expect no handle() signature or behaviour change. Bump it in isolation (its own PR, its own deploy) so that if something does break, the diff is unambiguous. Read the release diff as composer.json only; if src/ moved, widen your review.
Testing integration. Disable middleware you don't care about per-test with $this->withoutMiddleware(LogRequests::class) (class-scoped is nicer than disabling everything), and assert headers in one dedicated smoke test:
$this->getJson('/api/ping')
->assertHeader('X-Request-Id')
->assertHeader('Content-Type', 'application/json');
After the 2.1.3 bump, run this suite on the lowest and highest Laravel version you support — the package claims 11+, but only your app's tests prove it for your dependency graph. Also test the negative path: request with Accept: text/html against an API route should still return JSON.
Feature-flag and environment scoping. Don't register globally in testing/local if the middleware writes logs or hits the DB; a bootstrap/app.php conditional on $app->environment('production') keeps local errors readable.
illuminate/* to admit Laravel 11; it does not tell you whether the code was exercised against 11's bootstrap changes. Treat it as permission to install, not evidence of correctness — your smoke test is still the gate.app/Http/Middleware with a comment pointing at the upstream repo; you keep the licence (MIT) and lose the supply-chain risk.Kernel/bootstrap entry means it runs twice — harmless for header-setters, disastrous for anything that logs or mutates state.return. Each handle() must return the chained response. A dropped return yields a blank 200 and looks like a routing bug; route:list -v won't help because the route matched fine.route:list -v only shows route/group middleware, not global ones. To debug order, add Log::debug(static::class) at the top of handle() and read the sequence — fastest way to confirm prepend/append actually did what you assumed.terminate() or avoid statics altogether.X-Forwarded-* headers often die at the load balancer. Trust App\Http\Middleware\TrustProxies configuration before blaming the middleware.env() call inside it — your config or theirs — returns null after php artisan config:cache. Always read config via config('...'), and re-run config:clear when behaviour changes only in production.header() instead of the response object breaks under StreamedResponse and file downloads; check what the package does before putting it on a download route.handle(). If you need real config-driven behaviour, write your own 30-line middleware modelled on theirs — you'll spend less time than reverse-engineering an unmaintained package.How can I help you explore Laravel packages today?