What is Middleware in Express and How It Works
The Problem: Why Do We Need Middleware?
Imagine you are entering an airport.
Before boarding a plane, you pass through several checkpoints:
Security Check
Identity Verification
Baggage Check
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 objectres→ Response objectnext→ 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.