Middleware in Express.js
Middleware in Express refers to functions that execute in the ‘middle’ of a request/response cycle, typically before a matching route handler function is executed. Middleware functions have access to the request object (req
), the response object (res
), and the next()
function in the application's request-response cycle.
By implementing middleware, we can:
- Modify the
req
(request) orres
(response) objects before processing the route. - Redirect users or respond to requests before other routes are processed.
- Block clients from accessing specific routes.
- Log requests or handle logic before processing routes.
- Respond to requests for routes that do not exist (e.g., generate "404" errors).
- Handle exceptions that occur during route handler processing (e.g., generate "500" series errors).
Updating the req
Object
We can update the req
object in our middleware to include additional properties. For example, adding a log
property:
app.use((req, res, next) => {
let loggedItem = `Request from: ${req.get('user-agent')} [${new Date()}]`;
console.log(loggedItem);
req.log = loggedItem;
next();
});
app.get('/', (req, res) => {
res.send(`Hello - ${req.log}`);
});
Here, the middleware logs the request and adds a
log
property to thereq
object, which is then used in the route handler.
Restricting Route Access
Middleware can be used to restrict access to specific routes:
function randomDeny(req, res, next) {
let allowed = Math.floor(Math.random() * 2); // 0 or 1
if (allowed) {
next();
} else {
res.status(403).send('Access Denied');
}
}
app.get('/secure', randomDeny, (req, res) => {
res.send('Welcome!');
});
This middleware randomly allows or denies access to the
/secure
route. If denied, it sends a "403 Forbidden" response.
Handling 404 Errors
We can create a custom "404" error to handle requests for unknown routes:
app.use((req, res, next) => {
res.status(404).send("404 - We're unable to find what you're looking for.");
});
This middleware is placed after all other route handlers to ensure it only executes if no other route matches, sending a "404 Not Found" response.
Types of Middleware
Application-Level Middleware
Application-level middleware is bound to your entire application and runs with every request or only when matching a specified route.
app.use((req, res, next) => {
console.log('Application-level middleware');
next();
});
Router-Level Middleware
Router-level middleware works the same way as application-level middleware but is attached to a specific router instance:
const userRouter = express.Router();
userRouter.use((req, res, next) => {
console.log('userRouter Middleware!');
next();
});
Error-Handling Middleware
Error-handling middleware is defined with four parameters (err, req, res, next)
and is used for handling errors in the application:
app.get('/error-test', (req, res) => {
throw new Error('Error Test');
});
app.use((err, req, res, next) => {
res.status(500).send(`500 - ${err.message}`);
});
Built-In Middleware
Express provides built-in middleware functions for common tasks:
app.use(express.static('public')); // Serve static files
app.use(express.json()); // Parse JSON payloads
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded data