Beware of Middleware: Why Overusing Middleware Can Be Harmful

The article argues that while middleware can simplify handling cross‑cutting concerns in HTTP APIs, overusing it leads to tangled, untestable code, and proposes refactoring middleware logic into pure functions with explicit inputs and outputs to improve clarity and maintainability.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Beware of Middleware: Why Overusing Middleware Can Be Harmful

When building an HTTP API, developers often distinguish between route‑specific behavior (controllers and models) and behavior that applies to many routes, which is usually implemented as middleware. The author compares controllers to a lexer/parser that transforms unconstrained request data into a more constrained model representation.

Middleware, however, operates on the raw request and response objects, mutating them or causing side effects without returning meaningful values. Simple middleware examples such as rate limiting, authorization checks, and logging are presented, followed by Express code illustrating how they are typically chained.

const rateLimitingMiddleware = async (req, res) => {
  const ip = req.headers['ip'];
  db.incrementNRequests(ip);
  if (await db.nRequestsSince(Date.now() - 60000, ip) > 100) {
    return res.send(423);
  }
};

const authorizationMiddleware = async (req, res) => {
  const account = await db.accountByAuthorization(req.headers['authorization']);
  if (!account) { return res.send(401); }
};

const loggingMiddleware = async (req, res) => {
  if (Math.random() <= .1) {
    console.log(`request received ${req.method} ${req.path}
${req.body}`);
  }
};

app.use([rateLimitingMiddleware, authorizationMiddleware, loggingMiddleware].map(f => (req, res, next) =>
  f(req, res).then(() => next()).catch(err => next(err))
));

The author then suggests a different approach: write each operation as a pure function that receives explicit parameters and returns a result, then expose a thin middleware that translates the HTTP request into calls to those functions. This yields a "slim controller" that is easier to reason about.

const shouldRateLimit = async (ip) => {
  return await db.nRequestsSince(Date.now() - 60000, ip) < 100;
};

const isAuthorizationValid = async (authorization) => {
  return !!await db.accountByAuthorization(authorization);
};

const emitLog = (method, path, body) => {
  if (Math.random() < .1) {
    console.log(`request received ${method} ${path}
${body}`);
  }
};

const mw = async (req, res) => {
  const { ip, authorization } = req.headers;
  const { method, path, body } = req;
  if (await shouldRateLimit(ip)) { return res.send(423); }
  if (!await isAuthorizationValid(authorization)) { return res.send(401); }
  emitLog(method, path, body);
};

app.use((req, res, next) => {
  mw(req, res).then(() => next()).catch(err => next(err));
});

When additional requirements such as admin‑only logging and exempting admins from rate limits are introduced, the author shows how naive middleware ordering can cause bugs, and demonstrates a refactor that moves admin checks into the pure functions, reducing database calls and eliminating hidden dependencies.

const authorizationMiddleware = async (req, res) => {
  const account = await db.accountByAuthorization(req.headers['authorization']);
  if (!account) { return res.send(401); }
  req.isAdmin = account.isAdmin();
};

const rateLimitingMiddleware = async (req, res) => {
  if (req.isAdmin) return;
  const ip = req.headers['ip'];
  db.incrementNRequests(ip);
  if (await db.nRequestsSince(Date.now() - 60000, ip) > 100) { return res.send(423); }
};

const loggingMiddleware = async (req, res) => {
  if (req.isAdmin || Math.random() <= .1) {
    console.log(`request received ${req.method} ${req.path}
${req.body}`);
  }
};

Finally, the article reflects on the broader lesson: avoid letting frameworks dictate design; instead, write pure functions, pass required data explicitly, and return results, much like building a compiler. This mindset helps prevent the misuse of middleware and keeps codebases maintainable.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendmiddlewareHTTPsoftware designnodejsExpress
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.