Skip to main content

Command Palette

Search for a command to run...

What is Middleware in Express and How It Works

Updated
5 min read

The Problem: Why Do We Need Middleware?

Imagine you are entering an airport.

Before boarding a plane, you pass through several checkpoints:

  1. Security Check

  2. Identity Verification

  3. Baggage Check

  4. Boarding Gate

Only after passing these checkpoints can you enter the airplane.

Express applications work in a very similar way.

When a client sends a request to a server, we often need to:

  • Log the request

  • Check if the user is authenticated

  • Validate request data

  • Parse JSON data

If we write this code inside every route, our application becomes repetitive and difficult to maintain.

This is the problem Middleware solves.

What is Middleware?

Middleware is a function that runs between receiving a request and sending a response.

Think of middleware as a checkpoint.

Client Request
      |
      v
  Middleware
      |
      v
 Route Handler
      |
      v
Server Response

Middleware can:

  • Read request data

  • Modify request data

  • Stop the request

  • Send a response

  • Pass control to the next middleware

Express Request Lifecycle

When a request reaches the server, it follows a pipeline.

Request
   |
   v
Middleware 1
   |
   v
Middleware 2
   |
   v
Middleware 3
   |
   v
Route Handler
   |
   v
Response

Every middleware gets a chance to process the request before it reaches the route handler.

Middleware Syntax

A middleware function receives three parameters:

function middleware(req, res, next) {
  console.log("Middleware executed");
  next();
}

Parameters:

  • req → Request object

  • res → Response object

  • next → Function that passes control to the next middleware

Understanding next()

The next() function is one of the most important parts of middleware.

Without next(), the request gets stuck.

Example:

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

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

Output:

First Middleware
Home Page

Flow:

Request
   |
Middleware
   |
 next()
   |
Route Handler
   |
Response

What Happens Without next()?

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

Now the request never reaches the route handler.

Request
   |
Middleware
   |
STOP

The browser keeps waiting because Express does not know what to do next.

Types of Middleware in Express

Express provides different types of middleware.

1. Application-Level Middleware

This middleware is attached to the entire application.

Example:

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

This middleware runs for every request.

/login
/signup
/products
/profile

All routes pass through it.

2. Router-Level Middleware

Sometimes we want middleware for only specific routes.

Example:

const router = express.Router();

router.use((req, res, next) => {
  console.log("Router Middleware");
  next();
});

router.get("/profile", (req, res) => {
  res.send("Profile Page");
});

Only routes inside that router use this middleware.

3. Built-in Middleware

Express provides some middleware out of the box.

express.json()

Converts JSON request data into JavaScript objects.

app.use(express.json());

Example Request:

{
  "name": "Ashish"
}

Now you can access:

req.body.name

Without express.json(), req.body would be undefined.

express.static()

Used to serve static files.

app.use(express.static("public"));

Files inside the public folder become accessible directly.

Example:

public/logo.png

Access:

http://localhost:3000/logo.png

Middleware Execution Order

Middleware executes in the order it is registered.

Example:

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

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

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

Output:

Middleware 1
Middleware 2
Route Handler

Flow:

Request
   |
Middleware 1
   |
Middleware 2
   |
Route Handler
   |
Response

Real World Example 1: Logging Middleware

Suppose we want to know which routes users are accessing.

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

Output:

GET /products
POST /login
GET /profile

This is called Logging Middleware.

Real World Example 2: Authentication Middleware

Suppose some routes require login.

function authMiddleware(req, res, next) {
  const isLoggedIn = true;

  if (!isLoggedIn) {
    return res.status(401).send("Unauthorized");
  }

  next();
}

Using it:

app.get("/profile", authMiddleware, (req, res) => {
  res.send("Welcome User");
});

Flow:

Request
   |
Authentication Check
   |
   +--> Not Logged In
   |        |
   |    Unauthorized
   |
   +--> Logged In
            |
        Route Handler

Real World Example 3: Request Validation

Before creating a user, we can validate data.

function validateUser(req, res, next) {
  if (!req.body.name) {
    return res.status(400).send("Name Required");
  }

  next();
}

Using it:

app.post("/users", validateUser, (req, res) => {
  res.send("User Created");
});

This ensures invalid data never reaches the route handler.

Middleware Chain Example

Multiple middleware can work together.

app.get(
  "/profile",
  logger,
  authMiddleware,
  validateUser,
  (req, res) => {
    res.send("Profile Data");
  }
);

Execution Order:

Request
   |
Logger
   |
Authentication
   |
Validation
   |
Route Handler
   |
Response

This is called a Middleware Chain.

Why Middleware is Useful

Without middleware:

app.get("/profile", () => {
  // logging code
  // auth code
  // validation code
  // route logic
});

Every route would contain repeated code.

With middleware:

app.get(
  "/profile",
  logger,
  authMiddleware,
  validateUser,
  handler
);

The code becomes:

  • Cleaner

  • Reusable

  • Easier to maintain

  • Easier to debug

Conclusion

Middleware is one of the most powerful features of Express.

Think of it as a checkpoint in the request pipeline.

Whenever a request arrives, middleware gets an opportunity to inspect, modify, validate, or even stop the request before it reaches the route handler.

By separating logging, authentication, validation, and other common tasks into middleware, Express applications become cleaner, more organized, and easier to maintain.