Strip away all three frameworks' syntax and what's left is one shape, and it's worth holding it in your head before any of the framework-specific code shows up, because every single thing that follows is a variation on it.
A request for POST /returns/4471/approve enters at the top of a list of functions and moves down it, one at a time. Each function gets exactly two choices:
Something eventually produces a response — usually the route handler that actually approves the return — and once that happens, the response travels back up through every layer the request passed through, in the exact reverse order. That's the detail that makes this whole thing worth drawing as an onion rather than a straight line: each layer wraps everything beneath it, gets a turn on the way in, and gets a second turn on the way out, and those two turns are usually two different lines of the same function.
Every one of Counter's three builds needs the same handful of things applied to most requests: who's making this request, are they allowed to approve a return this large, log it either way, and — for the two requests a second that hit the mobile-dashboard SPA at a different origin — answer the browser's CORS preflight correctly. Bolt all of that onto the front of approveReturn directly and you'd be retyping the identical five or six lines into every handler that happens to need them, with no guarantee any two copies stay in sync six months later. Middleware sidesteps that entirely: each concern gets written once, ordered however the team decides, and reused by every route that needs it — none of it living inside the handler function itself.