# What is Middleware in Express and How It Works

If you’ve started learning Express.js recently, chances are you’ve already seen the term middleware everywhere. Initially, middleware feels slightly confusing because people keep saying things like “middleware runs before the route handler” or “middleware sits between request and response”. But honestly, once the request flow becomes clear, middleware starts feeling like one of the most important and powerful concepts in Express.js.

Almost every real backend application uses middleware heavily. Whether it’s authentication, logging, request validation, JSON parsing, or error handling, middleware is involved almost everywhere. In fact, a huge part of Express.js internally works because of middleware itself.

In this blog, we’ll understand what middleware actually is, where it sits in the request lifecycle, different types of middleware, how execution order works, what the `next()` function does, and some practical real-world middleware examples used in backend applications.

# What Middleware Actually Is

Middleware is basically a function that executes between the incoming request and the final response. Whenever a client sends a request to the Express server, middleware gets a chance to process that request before the route handler sends the response back.

A middleware function usually has access to:

*   Request object (`req`)
    
*   Response object (`res`)
    
*   Next middleware function (`next`)
    

Basic example:

```javascript
const express = require("express");

const app = express();

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

  next();
}

app.use(middleware);

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

Here, whenever a request comes to the server, the middleware executes first. After that, `next()` forwards the request to the next step in the pipeline.

# Where Middleware Sits in Request Lifecycle

One important thing beginners should properly understand is where middleware actually sits in the flow.

The request lifecycle usually looks like this:

*   Client sends request
    
*   Middleware executes
    
*   Route handler executes
    
*   Response gets sent back
    

So middleware acts like an intermediate processing layer before the final route logic runs.

This is exactly why middleware becomes useful for tasks like:

*   Authentication
    
*   Logging
    
*   Validation
    
*   Parsing request data
    
*   Error handling
    

because all these things usually need to happen before the final response is generated.

# Understanding the Role of `next()`

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

Whenever middleware finishes its current task, it calls `next()` to pass control to the next middleware or route handler.

Example:

```javascript
function logger(req, res, next) {
  console.log("Request received");

  next();
}
```

If `next()` is not called, the request gets stuck there and never moves forward.

For example:

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

Here, the request will hang because the middleware neither sends a response nor calls `next()`.

# Understanding Middleware Execution Order

Middleware execution order in Express.js is extremely important because middleware runs in the same sequence in which it is defined.

Example:

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

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

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

Output when visiting `/`:

```javascript
Middleware 1
Middleware 2
```

This sequence becomes very important in real applications because authentication middleware usually needs to run before protected routes, validation middleware often runs before database operations, and logging middleware may run globally before everything else.

# Application-Level Middleware

Application-level middleware applies to the entire Express application.

Example:

```javascript
app.use((req, res, next) => {
  console.log("Global middleware");

  next();
});
```

This middleware executes for every incoming request regardless of route.

Application-level middleware is commonly used for:

*   Logging
    
*   Authentication
    
*   Parsing JSON
    
*   Security checks
    

because these operations are usually needed globally across the application.

# Router-Level Middleware

Router-level middleware works only for specific routes or routers.

Example:

```javascript
const router = express.Router();

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

  next();
});

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

This middleware executes only for routes handled by that router.

Router-level middleware becomes useful when certain route groups need separate authentication or validation logic.

# Built-In Middleware in Express.js

Express also provides built-in middleware.

One very common example is:

```javascript
app.use(express.json());
```

This middleware parses incoming JSON request bodies.

Without this middleware, `req.body` would not properly contain JSON data sent by the client.

Example request:

```javascript
{
  "name": "Gaurang"
}
```

After using `express.json()`, the data becomes accessible through:

```javascript
req.body.name
```

Built-in middleware helps reduce repetitive backend setup work.

# Real-World Middleware Example: Logging

Logging middleware is extremely common in backend systems.

Example:

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

  next();
});
```

Possible output:

```javascript
GET /users
POST /login
```

This helps developers track incoming requests and debug backend behavior more easily.

# Real-World Middleware Example: Authentication

Authentication middleware protects routes from unauthorized access.

Example:

```javascript
function authMiddleware(req, res, next) {
  const token = req.headers.authorization;

  if (!token) {
    return res.send("Access denied");
  }

  next();
}
```

Protected route:

```javascript
app.get("/dashboard", authMiddleware, (req, res) => {
  res.send("Dashboard");
});
```

Here, the middleware checks whether the token exists before allowing access to the route.

# Real-World Middleware Example: Request Validation

Validation middleware checks incoming request data before processing it.

Example:

```javascript
function validate(req, res, next) {
  if (!req.body.email) {
    return res.send("Email required");
  }

  next();
}
```

Route:

```javascript
app.post("/register", validate, (req, res) => {
  res.send("User registered");
});
```

This prevents invalid data from reaching the actual route logic.

# Why Middleware Became So Important

Middleware became one of the core strengths of Express.js because it allows backend logic to remain modular and reusable.

Instead of writing authentication, logging, or validation logic repeatedly inside every route, middleware allows developers to separate these concerns cleanly into reusable functions.

This makes applications:

*   Cleaner
    
*   More scalable
    
*   Easier to maintain
    
*   Easier to debug
    

especially in large backend systems.

# Final Thoughts

Middleware is one of the most fundamental concepts in Express.js because it controls how requests move through the application before responses are sent back. Instead of directly jumping from request to route handler, Express allows middleware functions to process requests step by step.

Whether it’s logging requests, validating input, checking authentication, parsing JSON, or handling errors, middleware sits at the center of modern Express.js applications. Once the request flow and the role of `next()` become clear, understanding Express backend architecture becomes much easier overall.
