Express doesn't limit you to its built-in middleware — you can write your own functions to handle logging, validation, authentication, or anything else that needs to run before a route handler.
Syntax
app.<method>(path, (req, res, next) => { ... })
Example: a request-timing middleware
const express = require("express");
const app = express();
const requestTimer = (req, res, next) => {
req.startTime = Date.now();
next();
};
app.use(requestTimer);
app.get("/health", (req, res) => {
const elapsed = Date.now() - req.startTime;
res.send(`Health check handled in ${elapsed}ms`);
});
app.listen(5100, () => {
console.log("Server is Running");
});
Example: route-specific custom middleware
const checkApiKey = (req, res, next) => {
const key = req.headers["x-api-key"];
if (key !== "secret-123") {
return res.status(401).json({ error: "Invalid API key" });
}
next();
};
app.get("/secure-data", checkApiKey, (req, res) => {
res.json({ data: "This is protected" });
});
Test both routes in Postman — /health runs the timer middleware automatically, and
/secure-data only responds successfully when the correct x-api-key header is sent.
Why write custom middleware
- Encapsulates reusable logic (auth checks, logging, validation) in one place.
- Keeps route handlers focused on the actual business logic.
- Composable — chain several small middleware functions together per route.