If you've started learning Node.js and Express.js, you've probably seen app.use() in almost every project:
const express = require("express");
const app = express();
app.use(express.json());
At first glance, it may seem like just another Express function. But in reality, app.use() is one of the most important concepts in Express. It is responsible for building the request-processing pipeline by registering middleware.
In this article, we'll explore what app.use() is, why it's important, how it works internally, and common real-world use cases.
What is app.use()?
app.use() is a method provided by Express that registers middleware.
Middleware is simply a function that executes between receiving an HTTP request and sending the HTTP response.
Every incoming request passes through one or more middleware functions before it reaches the final route handler.
The syntax is:
app.use([path], middleware);
-
path(optional): URL path where the middleware should run. -
middleware: Function executed for matching requests.
A middleware function looks like this:
function middleware(req, res, next) {
console.log("Middleware executed");
next();
}
The next() function tells Express to continue processing the next middleware or route handler.
Why Do We Need Middleware?
Imagine an Express application without middleware.
app.get("/users", (req, res) => {
res.send("Users");
});
The request directly reaches the route handler.
But real-world applications need much more:
- Validate incoming requests
- Parse JSON
- Authenticate users
- Log requests
- Handle errors
- Enable CORS
- Serve static files
Instead of repeating this logic in every route, Express lets us write reusable middleware and register it using app.use().
How Express Processes Requests
Suppose we have:
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");
});
When a request comes to /, Express executes the following pipeline:
Incoming Request
│
▼
Middleware 1
│
▼
Middleware 2
│
▼
Route Handler
│
▼
Response Sent
Console Output:
Middleware 1
Middleware 2
Route Handler
Express simply walks through its middleware stack in the order the middleware was registered.
The Importance of next()
Every middleware receives three parameters:
(req, res, next)
Example:
app.use((req, res, next) => {
console.log("Request received");
next();
});
next() tells Express:
"I'm done. Move to the next middleware."
If you forget to call next(), Express stops processing.
Example:
app.use((req, res, next) => {
console.log("Stopped here");
});
Now every request hangs because Express never moves to the next middleware.
Global Middleware
Without specifying a path, middleware runs for every request.
app.use((req, res, next) => {
console.log(req.method, req.url);
next();
});
Requests:
GET /users
POST /login
DELETE /products/5
All three execute this middleware.
This is why logging middleware is usually global.
Path-Specific Middleware
Middleware can also be attached to a specific path.
app.use("/api", (req, res, next) => {
console.log("API Middleware");
next();
});
This middleware executes for:
GET /api/users
POST /api/login
PUT /api/orders
But not for:
GET /
GET /about
Express matches the URL prefix before executing the middleware.
app.use() Works for All HTTP Methods
Unlike app.get() or app.post(), app.use() doesn't care about the HTTP method.
app.use("/users", middleware);
Runs for:
GET /users
POST /users
PUT /users
DELETE /users
PATCH /users
This makes it perfect for middleware like authentication.
Real-World Use Cases
1. Parsing JSON
app.use(express.json());
Without this:
console.log(req.body);
Output:
undefined
With express.json():
{
"name": "John"
}
becomes
req.body = {
name: "John"
};
2. Logging Requests
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
Output:
GET /users
POST /login
3. Authentication
app.use((req, res, next) => {
if (!req.headers.authorization) {
return res.status(401).send("Unauthorized");
}
next();
});
Every request must pass authentication before reaching the routes.
4. CORS
const cors = require("cors");
app.use(cors());
This allows browsers to make requests from different origins.
5. Serving Static Files
app.use(express.static("public"));
Now:
public/logo.png
is available as:
http://localhost:3000/logo.png
6. Mounting Routers
Suppose we have:
// users.js
router.get("/", getUsers);
router.post("/", createUser);
Register it:
app.use("/users", userRouter);
Routes become:
GET /users
POST /users
This keeps applications modular and easier to maintain.
Execution Order Matters
Consider:
app.use(authMiddleware);
app.get("/dashboard", dashboard);
Every request to /dashboard is authenticated.
Now reverse them:
app.get("/dashboard", dashboard);
app.use(authMiddleware);
Now authentication never runs because the response is already sent.
Middleware executes strictly in the order it is registered.
Error-Handling Middleware
Express recognizes error middleware by four parameters.
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({
message: err.message
});
});
Whenever next(error) is called, Express skips normal middleware and jumps directly to this error handler.
app.use() vs app.get()
| Feature | app.use() |
app.get() |
|---|---|---|
| Purpose | Register middleware | Register a GET route |
| HTTP Methods | All methods | Only GET |
| Path Required | Optional | Required |
Calls next()
|
Yes | Usually sends response |
| Used For | Logging, auth, parsing, routers | Handling GET requests |
Example:
app.use("/users", middleware);
Handles:
GET /users
POST /users
PUT /users
DELETE /users
Whereas:
app.get("/users", handler);
Handles only:
GET /users
Best Practices
- Register middleware in the correct order.
- Always call
next()unless you're sending a response. - Keep middleware focused on a single responsibility.
- Use path-specific middleware when appropriate.
- Place error-handling middleware at the end of your middleware stack.
Conclusion
app.use() is the foundation of an Express application. It lets you build a request-processing pipeline where each middleware performs a specific task before handing control to the next one.
Whether you're parsing request bodies, authenticating users, logging requests, enabling CORS, serving static files, or organizing routes, app.use() is the mechanism that ties everything together.
Understanding how app.use() works—and especially how middleware order and next() affect request flow—is essential for writing clean, scalable, and maintainable Express applications.
If you're learning Express, mastering app.use() is one of the biggest steps toward understanding how Express handles requests under the hood.
Thanks for reading! 🚀 If you found this article helpful, consider sharing it with fellow developers who are learning Express.js.
Top comments (1)
How does app.use() handle middleware functions with multiple arguments, I've seen some examples but still unsure. Would love to hear your thoughts on this.