A request enters at the top of the chain and moves down, layer by layer. Each middleware does its job, then either:
Once something does generate a response (usually the final route handler), it travels back up through the same layers in reverse — which is why logging middleware often logs both the incoming request and the outgoing response status, from the same place in the code. This "wrap everything below me" shape is sometimes called the onion model: each middleware is a layer of the onion, the route handler is the core, and a single function call travels all the way down to the core and all the way back out.
Every real HTTP server needs a set of cross-cutting concerns applied to most or all requests — logging, authentication, compression, CORS headers, error handling — without hardcoding all of that into every single route handler. Middleware exists specifically to let those concerns be written once, in one place, and composed together in a declared order, rather than duplicated (or forgotten) at the top of every handler function.