Middleware
Middleware lets you wrap logic around your handlers: things like adding a header to every response, logging requests, or enforcing a rate limit. In Fast, middleware is just a function that receives the request and a $next callback.
Writing middleware
The simplest middleware is a plain callable. Call $next($request) to continue down the chain, then do whatever you like with the response before returning it:
use Fast\Http\Request;
use Fast\Http\Response;
use_middleware(static function (Request $request, callable $next): Response {
$response = $next($request);
return $response->withHeader('X-Powered-By', 'Fast');
});
You can also short-circuit. If you return without calling $next, the handler never runs, which is handy for auth gates or maintenance modes:
use_middleware(static function (Request $request, callable $next): Response {
if ($request->header('x-api-key') !== $expected) {
return json(['error' => 'unauthorized'], 401);
}
return $next($request);
});
If you prefer a class, implement Fast\Middleware\Middleware and pass an instance or class name to use_middleware(). The interface has the same shape: a method that takes the request and $next.
Middleware is global
Here is the one thing that surprises people coming from other frameworks: Fast has no per-route middleware API. Every middleware you register is global and runs for every request. This is a deliberate design choice that keeps the request path simple and predictable.
So how do you apply logic to just some routes? You scope it yourself inside the middleware, usually by checking the path or method:
use_middleware(static function (Request $request, callable $next): Response {
if (str_starts_with($request->path, '/admin')) {
// admin-only logic here
}
return $next($request);
});
Or, when the logic really belongs to one handler, just wrap it in that handler directly instead of reaching for middleware at all.
Keep global middleware cheap
Because every middleware runs on every request, you want them to be lightweight, especially for requests they do not actually care about. A middleware that does a quick path check and bails out early is fine. A middleware that does real work unconditionally, for a concern only some requests use, is a design smell in Fast.
This is the same principle behind Fast's opt-in subsystems: a request that does not touch a feature should not pay for it. Fast even asserts the size of its own global middleware stack in tests, so the framework holds itself to this rule too.
The order you register middleware in is the order it wraps the handler: the first registered is the outermost layer, so it sees the request first and the response last.
Next, let us look at the request and response objects your middleware and handlers work with, in Requests & Responses.