Middleware Patterns

The Shapes You'll See In Real Code

Middleware Patterns

A tour of the middleware patterns that show up across Express codebases.

4 min read Level 2/5 #express#middleware#patterns
What you'll learn
  • Recognize common patterns
  • Pick the right pattern for the job
  • Avoid common traps

Five patterns you’ll see repeatedly in real Express code.

1. Attach a Computed Property

function attachRequestId(req, res, next) {
  req.id = randomUUID();
  res.set("x-request-id", req.id);
  next();
}

Pre-compute something every later handler will use.

2. Tap Into the Response

function metrics(req, res, next) {
  const start = Date.now();
  res.on("finish", () => {
    histogramRequestDuration.observe(
      { route: req.route?.path, status: res.statusCode },
      Date.now() - start
    );
  });
  next();
}

Hook res.on("finish") for metrics, logging, audit trails.

3. Wrap a Sub-Chain

function transactional(handler) {
  return async (req, res, next) => {
    const tx = await db.beginTransaction();
    try {
      req.tx = tx;
      await handler(req, res, next);
      await tx.commit();
    } catch (err) {
      await tx.rollback();
      next(err);
    }
  };
}

app.post("/transfer", transactional(transferHandler));

Wrap the actual handler in pre/post logic. Useful for DB transactions.

4. Compose Multiple

function compose(...mws) {
  return mws;   // Express already accepts arrays
}

const adminWrite = compose(
  requireAuth,
  requireAdmin,
  audit("admin-write"),
);

app.post("/admin/users", adminWrite, createUser);

Express handles arrays automatically — middleware composition for free.

5. Branch on Content Type

function parseBody(req, res, next) {
  const type = req.get("content-type") ?? "";
  if (type.startsWith("application/json"))           return express.json()(req, res, next);
  if (type.startsWith("application/x-www-form-urlencoded")) return express.urlencoded()(req, res, next);
  if (type.startsWith("text/"))                       return express.text()(req, res, next);
  next();
}

Manually pick the right parser based on the request. Rarely needed — but the mechanism is good to know.

Antipatterns

Long Middleware Chains

If a route has 8 middlewares in front of the handler, it’s hard to read and slow to add to. Split logic into the handler itself or into well-named bundles.

Hidden Side Effects

Middleware that mutates req in surprising ways becomes a debug nightmare. Document what your middleware adds to req.

Sync File I/O

Don’t readFileSync in middleware — blocks the event loop on every request.

End of Chapter

You can now compose Express middleware with confidence. Next chapter: building real REST APIs.

Building REST APIs →