HTTP Middleware in a nutshell
Tags: php, middleware, architecture, webdev
Published: 2020-11-01 22:19:28

I wanted to point people to a simple and framework-agnostic explanation of middleware. I found none. I wrote one.


## What it looks like

The basic structure of an HTTP middleware implementation:
```php
function middleware(Request $request, callable $next): Response {

    // 1. Code that happens before the next middleware is invoked.
    // Note: this code may alter the Request

    // 2. Invoke the next middleware.
    // Note: this step is optional too
    $response = $next($request);

    // 3. Code that happens after the next middleware returns.
    // Note: this code may alter the Response

    return $response ;
}
```

At its core, an HTTP _middleware_ is a piece of code that returns a Response.


## How it works

We can see from the code snippet, that each middleware is a _wrapper_ around the next middleware.

An _execution pipeline_ is composed by adding layers of such decoupled wrappers, usually around an _app kernel_.

<figure>
![Middleware](https://www.slimframework.com/docs/v4/images/middleware.png)<figcaption>Layers of HTTP middleware, borrowed from [Slim Framework](https://www.slimframework.com/docs/v4/concepts/middleware.html)</figcaption>
</figure>

Each middleware receives a Request and a reference to the next middleware in the pipeline (also called a _handler_).

<!-- 
It interacts with the Request in a way, that's why it may be called a _handler_. Usually (but optionally) the execution is passed to the next middleware using the handler.
-->

A middleware can do 3 optional things:
1. alter the Request (by injecting meta information for the rest of the app or pre-processing resources)
2. delegate the execution to the next handler passing it the (altered) Request
3. alter the Response returned by the next handler, or produce a completely new Response

> 💡
> A middleware may choose not to invoke the next handler and terminate the pipeline. For example, this happens in an authorization middleware if a request is not authorized.

Finally, the pipeline is executed using a _middleware dispatcher_.
There is quite a number of dispatchers available and several frameworks ship with their own.


## What it is for

Middleware provides a means to decompose a complex solution into smaller self-contained pieces of code.
The app kernel (controllers and such) can focus on business logic and avoid being bloated by authorization, authentication, CORS, caching, routing and other concerns that can be moved to HTTP middleware.


## PSR-15

PSR-15 is a standard that defines two interfaces:
- [`MiddlewareInterface`](https://www.php-fig.org/psr/psr-15/#22-psrhttpservermiddlewareinterface)
- [`RequestHandlerInterface`](https://www.php-fig.org/psr/psr-15/#21-psrhttpserverrequesthandlerinterface)

According to this standard, a middleware from the first snippet would look like this:
```php
class Middleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $next
    ): ResponseInterface
    {

        // 1. Code that happens before the next middleware is invoked.
        // Note: this code may alter the Request

        // 2. Invoke the next middleware.
        // Note: this step is optional too
        $response = $next->handle($request);

        // 3. Code that happens after the next middleware returns.
        // Note: this code may alter the Response

        return $response ;
    }
}
```

The Request and Response interfaces come from the [PSR-7](https://www.php-fig.org/psr/psr-7/) standard.

To make it compatible with the first snippet which represents a _functional_ middleware, we can simply implement the `__invoke` magic method:
{% collapsible Code of the '__invoke' method %} 
```php
    public function __invoke(
        ServerRequestInterface $request,
        callable $next
    ): ResponseInterface {
        $handler = new class($next) implements RequestHandlerInterface {
            private $next;

            public function __construct(callable $next)
            {
                $this->next = $next;
            }

            public function handle(
                ServerRequestInterface $request
            ): ResponseInterface
            {
                return ($this->next)($request);
            }

        };
        return $this->process($request, $handler);
    }
```
{% endcollapsible %}

You can look up PSR-15 compatible middleware and dispatchers.


## Conclusion

We have seen that middleware is just a self-contained piece of code that returns a Response.
We can compose layers of middleware to create an app.

Here are some resources to dig deeper:
- [Slim Framework](https://www.slimframework.com/docs/v4/concepts/middleware.html)
- [a list of middlewares](https://github.com/middlewares)
- [and another list (packagist)](https://packagist.org/search/?tags=middleware)

> 👋
>
> There are also other explanations of _middleware_, with different meanings in different contexts. This one refers to HTTP request handling pipeline.
>