Skip to main content

Command Palette

Search for a command to run...

What is Middleware in Express and How It Works

Updated
4 min read
What is Middleware in Express and How It Works
A
Graduated In Computer Application (BCA) | Love Building Systems | Currently learning Full Stack Development with ChaiCode Web-Dev 26 Cohort

Think of middleware as "assembling line worker" in your web server. In express a request doesn't just hits the route and instantly get a request. It usually passes throught the several layers of code that can inspect, transform, or even reject this request before it reach its final destination.

What Middleware Is in Express

At its core, a middleware is just a function has access to the request req object, a response res object and the next middleware function in the application request - response cycle.

A middleware funciton can:

  • Execute any code
  • Make changes to the request and response object
  • End the request and response cycle
  • Call the next middleware in the stack using next() method

Where Middleware Sits in Request Lifecycle

Middleware lives in the middle between the moment the server recieve request from the client and the movment the server sends back a final resposne.

When a reqeust comes in, Express move it through the pipeline. If a middleware function doesn't end the cycle (by sending a response), it must pass control to the next function. If it doesn't do either, the request will simply hang and the client will eventually timeout.

Types of Middleware

Application Level Middleware

Thses are bound to the instance of the app object using app.use() or app.method() (where method is get, post etc.). They applye to the entire application or a specific paths across the app.

const app = express();

app.use((req, res, next) => {
  console.log("Time:", Date.now());
  next();
});

Router-level middleware

This works exactly like application level middleware, except it is bound to the express route express.Router(). This is useful for modularizing your code like all /api/users route having a specific auth middleware.

const router = express.Router();

router.use("/user/:id", (req, res, next) => {
  console.log("Request Type:", req.method);
  next();
});

Built-in mmiddleware

Express comes with a few built in functions, so you don't have to re-invent the wheel for a common tasks.

  • express.json(): parse incoming requests with json payload.
  • express.urlencoded(): parse incoming requests with url-encoded payloads.
  • express.static(): servers static assets like html, image etc.

Execution Order of Middleware

Middleware exection is top down. The order in which you defined your middleware in your code is excatly the order in which they execute.

If you place a logging middleware at the top of your file, it will log every single requests. If you place it after a route handler that sends a response, that middleware will never run for that route because the cycle will already terminated.

Role of next() Function

The next() function is the "baton" in a relay race. When called, it tell express to move to the successeding middleware function in the stack.

  • Parsing Control: if you dont call next(), the next function in the chain never executed.

  • Error Handling: if you pass an argument to the next(), like next(err), express skipp all remaining non-error handling middleware and jump straight to the error handling middleware.

Real-world Examples

1. Logging

Logging is the most common use case, it allow you to see who is visiting your site and what are they doing.

app.use((req, res, next) => {
  console.log(`\({req.method} request received at \){req.url}`);
  next();
});

2. Authentication

You can protect routes by checking if a user is logged in before them to processed to the sensitive data.

const protect = (req, res, next) => {
  if (req.headers.authorization === "secret-token") {
    next(); // User is authenticated
  } else {
    res.status(401).send("Unauthorized"); // Stop the cycle here
  }
};

app.get("/dashboard", protect, (req, res) => {
  res.send("Welcome to your dashboard");
});

3. Request validation

Before processing data (like user registration), middleware can check if the incomming data is valid.

app.post(
  "/register",
  (req, res, next) => {
    if (!req.body.email || !req.body.password) {
      return res.status(400).send("Missing email or password");
    }
    next();
  },
  (req, res) => {
    // Logic to save user to database
    res.send("User registered!");
  },
);

Summary

Express middleware functions act as an intermediary pipeline between a client’s request and the server’s final response. They have full access to the req and res objects, allowing them to modify data, execute code, or terminate the cycle early.

Key types include Application-level (global), Router-level (modular), and Built-in (e.g., express.json). Execution follows a strict top-down order, managed by the next() function, which hands off control to the subsequent handler. If next() isn't called, the request hangs. This pattern is fundamental for managing authentication, logging, and input validation cleanly and efficiently before reaching the core application logic in modern web apps.