Skip to content

Middleware Patterns Explained — Complete Beginner's Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Middleware Patterns Explained. We cover key concepts, practical examples, and best practices to help you master this topic.

Middleware patterns are functions that execute sequentially during the request-response cycle, enabling cross-cutting concerns like logging, authentication, and error handling in web applications.

What You'll Learn

By the end of this tutorial, you will understand what middleware is, how middleware patterns work in backend frameworks, and how to build your own middleware pipeline.

Why It Matters

Middleware is the backbone of every modern web framework. Express.js, Django, ASP.NET Core, and Flask all use middleware. Without it, every endpoint would duplicate code for authentication, Parsing, logging, and error handling.

Real-World Use

DodaTech's Doda Browser sync API uses middleware to authenticate every request, log access patterns, compress responses, and handle errors — all before the route handler runs.

Middleware Learning Path

flowchart LR
  A[HTTP Request] --> B[Middleware Pipeline]
  B --> C[Route Handler]
  C --> D[Response]
  B --> E{You Are Here}
  style E fill:#f90,color:#fff

Understanding Middleware

Think of middleware like a security checkpoint at an airport. Every passenger (request) passes through multiple stations: ID check, baggage scan, boarding pass verification. Each station can stop the passenger or let them through to the next station. Middleware works the same way for web requests.

How Middleware Works

Every middleware function receives three things: the request object, the response object, and a next function. The middleware does its job and either ends the request (sends a response) or calls next() to pass control to the next middleware.

function myMiddleware(req, res, next) {
  console.log("Middleware ran at:", new Date().toISOString());
  next();
}

Expected output:

Middleware ran at: 2026-06-28T10:30:00.000Z

Building a Simple Pipeline

A middleware pipeline chains multiple functions together. Each function runs in order, and the request flows through them like water through pipes.

const express = require("express");
const app = express();

// First middleware
app.use((req, res, next) => {
  console.log("1: Request started");
  next();
});

// Second middleware
app.use((req, res, next) => {
  console.log("2: Processing request");
  next();
});

// Route handler
app.get("/", (req, res) => {
  console.log("3: Route handler");
  res.send("Hello World");
});

app.listen(3000);

Expected output on visiting /:

1: Request started
2: Processing request
3: Route handler

Middleware Order Matters

The order you register middleware determines the execution sequence. If you register error-handling middleware before routes, it will never catch errors from those routes.

app.use((req, res, next) => {
  console.log("Always runs first");
  next();
});

app.get("/test", (req, res) => {
  res.send("Test route");
});

app.use((req, res) => {
  res.status(404).send("Not found - this runs last");
});

Expected output for visiting /unknown:

Always runs first
Not found - this runs last

Common Mistakes

  1. Forgetting to call next() — Middleware that does not call next() will hang the request forever. The client waits until timeout.

  2. Wrong order for error handlers — Error-handling middleware with four parameters must be registered after all routes, or it will never catch errors.

  3. Sending multiple responses — If you send a response and then call next(), you get a "headers already sent" error.

  4. Modifying req/res after passing to next middleware — If you modify the request or response asynchronously after calling next(), behavior becomes unpredictable.

  5. Not handling async errors — In Express 4, async middleware that throws without using try/catch will crash the server.

Practice Questions

  1. What is the purpose of the next function in middleware? The next function passes control to the next middleware in the pipeline. Without calling it, the request stalls.

  2. What happens if middleware does not call next() and does not send a response? The request hangs until the client's timeout expires, resulting in a poor user experience.

  3. How does middleware ordering affect request processing? Middleware runs in the order it is registered. Error handlers must be last; parsing middleware must be before route handlers.

  4. Challenge: Write middleware that measures and logs the time taken for each request.

app.use((req, res, next) => {
  const start = Date.now();
  res.on("finish", () => {
    console.log(`${req.method} ${req.url} took ${Date.now() - start}ms`);
  });
  next();
});

FAQ

What is middleware in web development?

Middleware is a function that runs during the request-response cycle. It can modify the request, modify the response, end the request, or pass control to the next middleware.

Do all frameworks use middleware?

Most modern frameworks use middleware or a similar concept. Express.js, Django (middleware), ASP.NET Core (middleware), Flask (before/after request), and Koa all have middleware pipelines.

Can middleware send a response directly?

Yes. If middleware calls res.send(), res.json(), or similar, it ends the request-response cycle. Subsequent middleware will not run.

How many middleware functions can a request pass through?

There is no hard limit, but performance degrades with hundreds of middleware functions. Typical applications use 5-15 middleware functions per request.

Is middleware specific to HTTP servers?

No. Middleware patterns exist in many contexts: Redux uses middleware for side effects, socket.io uses middleware for authentication, and Kafka uses interceptors that work like middleware.

Mini Project

Build a middleware pipeline that logs request details, authenticates via API key, and handles errors for a simple book API. Create three middleware functions: one for logging, one for auth, one for error handling.

const express = require("express");
const app = express();

function logger(req, res, next) {
  console.log(`${new Date().toISOString()} ${req.ip} ${req.method} ${req.url}`);
  next();
}

function authenticator(req, res, next) {
  const key = req.headers["x-api-key"];
  if (!key || key !== "secret-key-123") {
    return res.status(401).json({ error: "Invalid API key" });
  }
  next();
}

function errorHandler(err, req, res, next) {
  console.error("Error:", err.message);
  res.status(500).json({ error: "Internal server error" });
}

app.use(logger);
app.use(authenticator);
app.use(express.json());

app.get("/api/books", (req, res) => {
  res.json([{ id: 1, title: "Dune" }]);
});

app.use(errorHandler);

app.listen(3000, () => console.log("Server running on port 3000"));

What's Next

Now that you understand middleware basics, explore specific middleware types like logging, authentication, and error handling in Express.js. Then learn about async middleware, chaining, and third-party middleware integration.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro