What is Middleware in Express and How It Works

When building applications with Express.js, one of the most important concepts you’ll encounter is middleware.
Middleware is what makes Express powerful, flexible, and easy to scale.
Whether it’s logging requests, checking authentication, validating input, or handling errors, middleware sits in the middle of the request-response cycle and controls how requests move through your application.
In this article, we’ll understand what middleware is, how it works internally, different types of middleware, and why it plays such a critical role in Express applications.
What is Middleware in Express?
Middleware in Express is simply a function that runs between receiving a request and sending a response.
It gets access to:
req→ the incoming requestres→ the response objectnext()→ a function that passes control to the next middleware
Basic syntax:
const middleware = (req, res, next) => {
console.log("Middleware executed");
next();
};
Middleware acts like a checkpoint in the request lifecycle.
Instead of sending every request directly to the route handler, Express allows middleware to inspect, modify, block, or process requests before they reach the final response.
Middleware in the Request Lifecycle
Here’s how a request typically flows in Express:
Client Request
↓
Middleware 1
↓
Middleware 2
↓
Middleware 3
↓
Route Handler
↓
Response Sent
Think of middleware as workers standing in a pipeline.
Each worker performs a task and then either:
passes the request forward using
next()stops the request and sends a response
Middleware Execution Flow
A middleware chain works sequentially.
Example:
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("Home Route");
});
When a request hits /, the output becomes:
First middleware
Second middleware
Then the route handler sends the final response.
Role of next() Function
The next() function is extremely important in Express middleware.
It tells Express:
“Move to the next middleware or route handler.”
Example:
const logger = (req, res, next) => {
console.log(req.method, req.url);
next();
};
Without calling next(), the request gets stuck and never reaches the next step.
Example of a blocked request:
app.use((req, res, next) => {
console.log("Request received");
});
Since next() is missing and no response is sent, the browser keeps loading forever.
Types of Middleware in Express
Express supports multiple types of middleware.
1. Application-Level Middleware
Application-level middleware is attached directly to the Express app using:
app.use()
Example:
app.use((req, res, next) => {
console.log("Application middleware");
next();
});
This middleware runs for every request unless restricted to a specific path.
Example with path:
app.use("/admin", (req, res, next) => {
console.log("Admin middleware");
next();
});
Now it only runs for routes starting with /admin.
2. Router-Level Middleware
Router-level middleware works specifically with Express routers.
Example:
const router = express.Router();
router.use((req, res, next) => {
console.log("Router middleware");
next();
});
This middleware only affects routes inside that router.
Useful for:
admin panels
API modules
authentication-protected sections
3. Built-in Middleware
Express already provides some middleware functions.
Example:
- JSON Middleware
app.use(express.json());
This parses incoming JSON request bodies.
Without it:
req.body
would be undefined.
- Static File Middleware
app.use(express.static("public"));
This serves static files like:
images
CSS
JavaScript files
Execution Order of Middleware
Middleware executes in the exact order it is defined.
Example :
app.use((req, res, next) => {
console.log("Middleware 1");
next();
});
app.use((req, res, next) => {
console.log("Middleware 2");
next();
});
Output:
Middleware 1
Middleware 2
If the order changes, behavior changes too.
This is extremely important for things like:
authentication
body parsing
logging
validation
For example, body parsing middleware should run before validation middleware.
Real-World Middleware Examples
Middleware becomes powerful when used for common backend tasks.
Logging Middleware
Used to log request details.
Example:
app.use((req, res, next) => {
console.log(`\({req.method} \){req.url}`);
next();
});
Output:
GET /users
POST /login
This helps in debugging and monitoring traffic.
Authentication Middleware
Checks whether a user is authenticated.
Example :
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({
message: "Unauthorized"
});
}
next();
};
Protected route:
app.get("/profile", authMiddleware, (req, res) => {
res.send("Protected Profile");
});
Request Validation Middleware
Validates incoming request data before processing.
Example:
const validateUser = (req, res, next) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({
message: "All fields are required"
});
}
next();
};
Route usage:
app.post("/users", validateUser, (req, res) => {
res.send("User created");
});
Why Middleware is So Important
Middleware is the backbone of Express applications.
It allows developers to:
separate logic cleanly
reuse functionality
create scalable APIs
control request flow efficiently
Almost every major backend feature in Express is implemented using middleware.
Examples include:
authentication
file uploads
request parsing
CORS handling
logging
rate limiting
error handling
Conclusion
Middleware is one of the most powerful concepts in Express.js.
It acts as a bridge between the incoming request and the final response, allowing you to process requests step by step.
By understanding:
how middleware works
execution order
the role of
next()different middleware types
you gain much better control over how your Express applications behave.
As your applications grow, middleware becomes essential for writing clean, organized, and maintainable backend code.



