Skip to main content

Command Palette

Search for a command to run...

What is Middleware in Express and How It Works

Published
5 min read
What is Middleware in Express and How It Works

Middleware is one of the most important concepts in building scalable, flexible web applications and APIs with Express.js. Middleware is a way to tap into the request/response cycle and do some logic before the requests hit your main route handlers or before responses are sent back to clients.

Think of middleware like checkpoints or stations on an assembly line. Every incoming request passes through one or more middleware functions before returning a response.

Let’s see what middleware is, where it fits into Express and see practical examples for common use cases.

What Is Middleware in Express?

In Express, middleware is just a function that gets the request and response objects (like a route handler), but it can do three main things:

  1. Modify or post the request before it gets to the route handler.

  2. Before returning a response to the client, read or edit it.

  3. Decide if request should continue, hand control to next middleware, or end request/response cycle.

Basic Middleware Signature:

function myMiddleware(req, res, next) {
  // ...logic here
  next(); // Pass control to the next middleware or route
}

Notice the third argument: next. This is a special function provided by Express to move the request along the pipeline.

Where Middleware Sits in the Request Lifecycle

When a request hits your Express app:

  1. The request enters Express.

  2. It passes through any configured middleware functions in order.

  3. Eventually it reaches a route handler (if not ended early by middleware).

  4. The response is sent and the cycle ends.

Analogy:
Imagine a package traveling through an assembly line.
Each station (middleware) can check it, add a sticker, reject it, wrap it, or send it to the next station.
Finally, the package is delivered (response sent back).

Types of Middleware in Express

Express supports several types of middleware, each suited for different use cases.

1. Application-Level Middleware

These are functions applied to the whole app.

Example: Logging Middleware

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

app.use((req, res, next) => {
  console.log(`\({req.method} \){req.url}`);
  next();
});
  • app.use() applies the middleware to every request.

  • You must call next() to let the pipeline continue!

2. Router-Level Middleware

Middleware attached to specific routers or sets of routes.

Example: Protect Admin Routes

const express = require("express");
const adminRouter = express.Router();

adminRouter.use((req, res, next) => {
  if (!req.user || !req.user.isAdmin) {
    return res.status(403).send("Forbidden");
  }
  next();
});

adminRouter.get("/dashboard", (req, res) => {
  res.send("Admin Dashboard");
});

You attach the router to the app (app.use("/admin", adminRouter);) and only /admin routes will use that middleware.

3. Built-In Middleware

Express provides built-in middleware for common needs:

  • express.json() – Automatically parses incoming JSON requests.

  • express.urlencoded() – Parses URL-encoded form data.

  • express.static() – Serves static files like images or CSS.

Example:

app.use(express.json());

Now every incoming JSON body is parsed and accessible as req.body.

Execution Order of Middleware in Express

Order matters!
Express runs middleware in the order you define it.

app.use(middlewareA);
app.use(middlewareB);

app.get("/hello", routeHandler);
  • A request goes to middlewareA first, then middlewareB, then routeHandler.

  • If any middleware does not call next(), the request halts immediately and never reaches the route.

Middleware can:

  • Continue to the next function (next())

  • End the response (res.send())

  • Pass errors to the next error handler (next(err))

Role of the next() Function

  • next() is how you tell Express to “move on” to the next thing in the pipeline.

  • Without next(), the request will hang forever (unless you send a response).

  • You can call next(err) to signal an error and invoke error-handling middleware.

Middleware Execution Sequence: A Visual Example

Consider:

app.use((req, res, next) => {
  console.log("First middleware");
  next();
});

app.use((req, res, next) => {
  console.log("Second middleware");
  next();
});

app.get("/", (req, res) => {
  res.send("Done!");
});

Request to / will print:

First middleware
Second middleware

And then respond with Done!.

If you send a response inside middleware (e.g., res.send("403 Forbidden")), Express skips all the remaining middleware and routes.

Real-World Examples of Middleware

1. Logging All Requests

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

2. Authentication “Checkpoint”

function authenticate(req, res, next) {
  if (!req.user) {
    return res.status(401).send("Unauthorized");
  }
  next();
}
app.use("/dashboard", authenticate);

3. Request Validation

function validateUserInput(req, res, next) {
  if (!req.body.username || !req.body.password) {
    return res.status(400).json({ error: "Missing fields" });
  }
  next();
}
app.post("/signup", validateUserInput, (req, res) => {
  // Create user
});
  • If validation fails, middleware ends the cycle with a 400 response.

  • If it passes, the actual signup route runs.

Request Pipeline Analogy

Imagine each call as passing through a pipeline of functions:

  • Middleware 1: Add user info if logged in

  • Middleware 2: Validating input.

  • Middleware 3: Log the request

  • Route Handler: Handle the request and send the response.

Each middleware can inspect, modify or even stop the request before it finally reaches the actual route handler.

Best Practices & Notes

  • Put global middleware (e.g logging, json parsing) as soon as possible.

  • Use router-level middleware for features that are only needed on some route groups (like admin auth)

  • Always call ​ unless you send a response or encounter an error.

  • The order of middleware is important . Earlier middleware can influence later middleware and route handlers .

  • Middleware can also return errors / responses, which stops the pipeline.line.

Conclusion

In Express, middleware is a chain of functions that execute during the lifecycle of a request to the server. It enables you to:

  • Add features and behaviour to your app without changing route handlers

  • Reuse common processing logic (authentication, logging, input validation, etc.)

  • Manage the flow of requests and responses

Understanding middleware can give you a very powerful way to control every request in your Express application. It makes your code modular, testable and maintainable.

What is Middleware in Express and How It Works