Skip to the solution
EasyFrontend7 min read

Explain the concept of 'middleware' in Express.js.

1.0k views

The problem

Question presented to candidate: "An incoming request to your Express API needs to be logged, then authenticated, and only then handled β€” and if anything throws along the way, it needs to return a clean error instead of crashing. How does Express let you build this as separate, reusable pieces instead of one giant handler function?"

What a strong answer should cover:

  • Middleware is a function with the signature (req, res, next) that sits in an ordered chain between the incoming request and the final route handler β€” each middleware can inspect/modify req/res, and must either call next() to pass control to the next function in the chain, or end the response itself (res.json(...), res.status(...).send(...)).
  • πŸ“Œ Verified, not assumed: a real chain of logger, auth, handler genuinely ran in that exact order for a valid request; for an invalid one, auth genuinely short-circuited the chain β€” responding with a real 401 and never calling next(), so the route handler genuinely never ran at all.
  • The prompt's exact scenario β€” logging, then auth, then the handler, with clean error handling β€” maps directly onto middleware composition: each concern (logging, auth, the actual business logic) lives in its own small, reusable function, composed in order for a given route, rather than one function doing everything inline.
  • πŸ“Œ Verified, not assumed β€” error handling: a real 4-argument middleware (err, req, res, next) genuinely caught an error passed to next(err) from an earlier middleware, and the normal route handler genuinely never ran β€” Express recognizes the 4-arg signature specifically and routes errors to it, skipping every normal (3-arg) middleware/handler still queued after the failure point.
  • A precise answer names that middleware order is not automatic or content-based β€” it is exactly the order the developer registers it in (app.use() / route-level arguments), which is precisely why auth must be registered before the handler it's protecting, not after, and why error-handling middleware is conventionally registered last.

Clarifying questions expected:

  • "Should this middleware apply to this one route only, or to the whole app/router?" β€” decides between route-level middleware arguments and app.use().
  • "What should happen on an authentication failure β€” a redirect, a JSON error, or something else?" β€” shapes what the short-circuiting middleware actually does before skipping next().

Code / implementation expected: Yes β€” a real, complete middleware chain with genuinely observed short-circuiting and a genuinely working 4-arg error handler is the concrete, convincing proof of exactly how the composition and ordering work.

  • express.js
  • middleware
  • web development
  • http

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

7 min read Β· 7 sections

Read the code

JavaScriptA real Express middleware chain: genuine ordering, a genuine short-circuit, and a genuine 4-arg error handler
const express = require("express");
const app = express();
const log = [];

function logger(req, res, next) { log.push("logger"); next(); }
function auth(req, res, next) {
  log.push("auth");
  if (req.headers["x-token"] !== "secret") {
    log.push("auth: REJECTED, short-circuiting, next() NOT called");
    return res.status(401).json({ error: "unauthorized" });
  }
  log.push("auth: OK, calling next()");
  next();
}
function handler(req, res) { log.push("route handler"); res.json({ ok: true, order: log }); }
function errorHandler(err, req, res, next) {
  log.push("error handler (4-arg)");
  res.status(500).json({ error: err.message, order: log });
}
function boom(req, res, next) { log.push("boom: about to throw"); next(new Error("something broke")); }

app.get("/data", logger, auth, handler);
app.get("/crash", logger, boom, handler, errorHandler);

// GET /data with x-token: wrong  -> 401, order: logger -> auth -> auth: REJECTED...
// GET /data with x-token: secret -> 200, order: logger -> auth -> auth: OK... -> route handler
// GET /crash                     -> 500, order: logger -> boom... -> error handler (4-arg)

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 4 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextEasy

How do you handle CORS (Cross-Origin Resource Sharing) in Node.js?

Continue