Express Middleware Patterns — Complete Guide with Examples
In this tutorial, you will learn about Express Middleware Patterns. We cover key concepts, practical examples, and best practices to help you master this topic.
Express middleware patterns give you fine-grained control over request processing through application-level, router-level, and error-handling middleware, each serving a distinct purpose in your web application.
What You'll Learn
By the end of this tutorial, you will understand the four types of Express middleware, when to use each type, and how to combine them for clean, maintainable code.
Why It Matters
Express is the most popular Node.js framework, and middleware is its core architectural pattern. Mastering Express middleware lets you build secure, performant APIs the same way DodaTech builds its production services.
Real-World Use
The DodaZIP file conversion API uses Express middleware at the application level for CORS and compression, at the router level for API Versioning, and at the error level for consistent error responses.
Express Middleware Types
flowchart LR
A[Express Middleware] --> B[Application-level]
A --> C[Router-level]
A --> D[Error-handling]
A --> E[Built-in]
B --> F{You Are Here}
style F fill:#f90,color:#fff
Application-Level Middleware
Application-level middleware is bound to the app object using app.use() or app.VERB(). It runs on every request or every matching request.
const express = require("express");
const app = express();
// Runs on every request
app.use((req, res, next) => {
req.requestTime = Date.now();
next();
});
// Runs only on GET /hello
app.get("/hello", (req, res) => {
res.json({ message: "Hello", time: req.requestTime });
});
app.listen(3000);
Expected output for GET /hello:
{"message": "Hello", "time": 1722189000000}
Router-Level Middleware
Router-level middleware works identically to application-level middleware but is bound to an instance of express.Router(). This lets you group routes and their middleware together.
const express = require("express");
const router = express.Router();
function validateId(req, res, next) {
const id = Number(req.params.id);
if (isNaN(id)) {
return res.status(400).json({ error: "Invalid ID" });
}
req.id = id;
next();
}
router.get("/users/:id", validateId, (req, res) => {
res.json({ userId: req.id });
});
const app = express();
app.use("/api", router);
app.listen(3000);
Expected output for GET /api/users/abc:
{"error": "Invalid ID"}
Expected output for GET /api/users/42:
{"userId": 42}
Error-Handling Middleware
Error-handling middleware has four parameters instead of three: err, req, res, next. Express recognizes it by the four parameters and calls it when any middleware or route throws an error or calls next(err).
const express = require("express");
const app = express();
app.get("/data", (req, res, next) => {
try {
const data = JSON.parse('{invalid json}');
res.json(data);
} catch (err) {
next(err);
}
});
app.use((err, req, res, next) => {
console.error("Error caught:", err.message);
res.status(500).json({
error: "Something went wrong",
details: err.message
});
});
app.listen(3000);
Expected output:
{"error": "Something went wrong", "details": "Unexpected token i in JSON at position 1"}
Built-in Middleware
Express provides several built-in middleware functions that handle common tasks.
const express = require("express");
const app = express();
// Parse JSON bodies
app.use(express.json());
// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));
// Serve static files
app.use(express.static("public"));
app.post("/submit", (req, res) => {
res.json({ received: req.body });
});
app.listen(3000);
Expected output for POST /submit with {"name": "Alice"}:
{"received": {"name": "Alice"}}
Common Mistakes
Using app.use() for method-specific routes —
app.use()matches all HTTP methods. Useapp.get(),app.post(), etc. for method-specific handling.Placing error middleware before routes — Error-handling middleware must be registered after all routes to catch errors from them.
Forgetting express.json() for POST bodies — Without
express.json(),req.bodyis undefined for JSON payloads.Not calling next() with errors — Pass errors to
next(err)rather than throwing them directly, or Express 4 will crash.Modifying req or res after sending response — Once
res.send()is called, further modifications cause errors.
Practice Questions
What is the difference between app.use() and app.get()?
app.use()matches all HTTP methods for a given path.app.get()matches only GET requests.How does Express distinguish error-handling middleware from regular middleware? Error-handling middleware has exactly four parameters: err, req, res, next.
When would you use router-level middleware instead of application-level? When specific routes (like admin endpoints) need special handling that other routes do not need.
Challenge: Create a router for admin endpoints that checks for an admin token before allowing access.
const router = express.Router();
router.use((req, res, next) => {
if (req.headers["x-admin-token"] !== "admin-secret") {
return res.status(403).json({ error: "Admin access required" });
}
next();
});
router.get("/dashboard", (req, res) => res.json({ users: 150 }));
module.exports = router;
FAQ
Mini Project
Build an Express API with three levels of middleware: application-level logging, router-level authentication for admin routes, and a global error handler.
const express = require("express");
const app = express();
app.use(express.json());
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
const adminRouter = express.Router();
adminRouter.use((req, res, next) => {
if (req.headers.authorization !== "Bearer admin-token") {
return res.status(401).json({ error: "Unauthorized" });
}
next();
});
adminRouter.get("/users", (req, res) => {
res.json([{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]);
});
app.use("/admin", adminRouter);
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});
app.listen(3000);
What's Next
Now that you understand Express middleware types, explore how to build logging middleware for production applications. Then learn about implementing authentication as middleware in Express.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro