Skip to main content

Middleware: The Onion

intermediate18 min readLesson 119 of 143

Cross-cutting concerns โ€” logging, auth, parsing โ€” as composable layers around every handler.

Every route needs some of the same work: parse the body, check auth, log timing, catch errors. Middleware is the pattern that factors it out: each layer wraps the next.

The shape

function logger(req, res, next) {
  const start = Date.now();
  res.on("finish", () => console.log(req.method, req.url, Date.now() - start + "ms"));
  next();
}

A middleware receives (req, res, next). It can act before next() (setup), after (response finished), or never call next() (it is the response โ€” auth denial). Nesting them forms an onion: request enters layer 1, 2, 3, handler, then unwinds back out.

The pipeline by hand

function pipeline(middlewares, handler) {
  return (req, res) => {
    let i = 0;
    const next = (err) => {
      if (err) return onError(res, err);
      const mw = middlewares[i++];
      if (mw) mw(req, res, next);
      else handler(req, res);
    };
    next();
  };
}

next is the trick: it's a closure over position i, so each call advances exactly one layer. Express's app.use() builds precisely this list.

Middlewares worth writing

  • Request logging (above) โ€” you'll write this in every job
  • Body parser โ€” assemble chunks, parse JSON, attach req.body, 400 on garbage
  • Auth guard โ€” verify session/token, attach req.user, 401 early
  • Error boundary โ€” the outermost layer's try/catch: one 500 shape for the whole app, logged once
  • Rate limiter โ€” keyed by IP/user, before the handler touches the DB

Ordering is semantics

auth before handler (obviously), but also before anything that touches req.user. bodyParser before anything reading req.body. Errors thrown inside deep layers must reach the outermost catch โ€” that's why the pipeline passes err through next(err) instead of letting each layer catch (and swallow) its own.

Now practice

Middleware Pipeline โ€” PracticeBuild the onion: a working pipeline runner, ordered guards, and an error boundary that never leaks.3 challenges ยท ยท ~20 min