What is Middleware in Express and How It Works

Software Engineer | Passionate about Web Development, DSA & Problem Solving. I write simple, practical tech blogs to help developers learn and grow. Exploring JavaScript, C++, Backend & Modern Web Technologies.
Introduction
Modern web applications handle far more than just sending responses to users. Before a request reaches its final destination, it often passes through multiple layers of processing such as authentication, logging, validation, parsing data, and error handling. Managing all these responsibilities directly inside route handlers would quickly make applications messy, repetitive, and difficult to maintain. This is where middleware becomes one of the most powerful features of Express.js.
Middleware acts as an intermediate layer between the incoming request and the final response. It allows developers to process requests step by step, creating a clean and modular architecture. Whether it is checking if a user is authenticated, logging API activity, validating request data, or transforming incoming payloads, middleware provides a structured way to handle these tasks efficiently.
Express.js is heavily built around the middleware concept, and understanding how middleware works is essential for building scalable backend applications. Middleware is also one of the most frequently discussed topics in Node.js interviews because it demonstrates how Express internally manages request processing.
In this blog, we will explore what middleware is, where it fits in the request lifecycle, the different types of middleware available in Express, and how middleware execution works using practical examples and clear analogies.
What Middleware is in Express
Middleware in Express is a function that has access to:
The request object (
req)The response object (
res)The
next()function
Middleware sits between the client request and the final route handler. It can:
Modify request data
Execute logic
End the request-response cycle
Pass control to the next middleware
In simple terms, middleware acts like a checkpoint during request processing.
Example
const express = require('express');
const app = express();
const middleware = (req, res, next) => {
console.log("Middleware executed");
next();
};
app.get('/', middleware, (req, res) => {
res.send("Home Page");
});
app.listen(3000);
Explanation :
A middleware function is created.
When the
/route is requested:Middleware executes first
Console logs message
next()passes control forwardRoute handler sends response
Real-World Analogy
Think of middleware like an airport security checkpoint:
Passenger = Request
Security check = Middleware
Boarding gate = Route handler
Where Middleware Sits in the Request Lifecycle
Every request in Express follows a lifecycle:
Client sends request
Middleware processes request
Route handler executes
Response sent back
Middleware acts as a processing pipeline.
Example
app.use((req, res, next) => {
console.log("Request received");
next();
});
app.get('/about', (req, res) => {
res.send("About Page");
});
Explanation:
app.use()registers middleware globallyEvery request passes through it first
Middleware logs request information
Request then moves to route handler
Request Pipeline Analogy
Imagine a factory assembly line:
Raw material enters → request
Different stations process it → middleware
Final product exits → response
Each middleware performs a specific task before passing control forward.
Application-Level Middleware
Application-level middleware is attached directly to the Express app using:
app.use()
It runs for all requests or specific routes.
Example
app.use((req, res, next) => {
console.log(`\({req.method} \){req.url}`);
next();
});
Explanation :
Logs request method and URL
Executes for every incoming request
Useful for logging and monitoring
Practical Use Cases
Logging
Authentication
Rate limiting
Router-Level Middleware
Router-level middleware works only for specific routers instead of the entire application.
Example
const router = express.Router();
router.use((req, res, next) => {
console.log("Router middleware");
next();
});
router.get('/profile', (req, res) => {
res.send("User Profile");
});
app.use('/user', router);
Explanation :
Middleware runs only for
/userroutesKeeps code modular and organized
Useful in large applications
Before vs After
| Without Router Middleware | With Router Middleware |
|---|---|
| Repeated logic | Centralized logic |
| Hard to maintain | Cleaner structure |
Built-in Middleware in Express
Express provides built-in middleware for common tasks.
Common built-in middleware:
express.json()express.urlencoded()express.static()
Example
app.use(express.json());
app.post('/data', (req, res) => {
console.log(req.body);
res.send("JSON received");
});
Explanation :
express.json()parses incoming JSON dataParsed data becomes available in
req.bodyEssential for REST APIs
Built-in Middleware Comparison
| Middleware | Purpose |
|---|---|
| express.json() | Parse JSON |
| express.urlencoded() | Parse form data |
| express.static() | Serve static files |
Execution Order of Middleware
Middleware executes in the order it is defined.
This order is extremely important because one middleware may depend on another.
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) => {
res.send("Route Handler");
});
Explanation :
Execution order:
Middleware 1
Middleware 2
Route handler
If next() is not called, request stops there.
Real-World Analogy
Think of middleware like checkpoints in a race:
Each checkpoint must clear the runner
Missing one stops progress
Role of next() Function
The next() function passes control to the next middleware or route handler.
Without next(), the request hangs unless a response is sent.
Example
app.use((req, res, next) => {
console.log("Checking request");
next();
});
app.get('/', (req, res) => {
res.send("Request completed");
});
Explanation :
Middleware executes first
next()forwards requestRoute handler completes response cycle
Before vs After
| Without next() | With next() |
|---|---|
| Request stuck | Request continues |
| No response | Proper flow |
Real-World Middleware Examples
Middleware is widely used in production applications.
Logging Middleware
app.use((req, res, next) => {
console.log(`${req.method} request received`);
next();
});
Authentication Middleware
const auth = (req, res, next) => {
const isLoggedIn = true;
if (isLoggedIn) {
next();
} else {
res.send("Unauthorized");
}
};
Request Validation Middleware
const validate = (req, res, next) => {
if (!req.body.name) {
return res.send("Name is required");
}
next();
};
Explanation :
Logging tracks requests
Authentication protects routes
Validation checks request data before processing
Practical Benefits
| Middleware Type | Purpose |
|---|---|
| Logging | Monitoring |
| Authentication | Security |
| Validation | Data integrity |
Conclusion
Middleware is one of the most important concepts in Express.js because it defines how requests are processed before reaching the final route handler. By acting as checkpoints in the request-response cycle, middleware enables developers to build modular, reusable, and maintainable backend applications.
We explored how middleware fits into the request lifecycle, the different types of middleware available in Express, and how execution order affects application behavior. We also examined the critical role of the next() function and looked at practical middleware examples such as logging, authentication, and validation.
The key takeaway is that middleware is not just a feature—it is the foundation of how Express applications operate. Understanding middleware deeply helps developers structure applications more efficiently, reduce code duplication, and implement scalable backend architectures.
Whether you are building REST APIs, handling authentication, or processing incoming data, mastering middleware is an essential step toward becoming a confident Node.js backend developer.



