Express JS custom middleware
In Express.js, middleware functions are functions that have access to the req
(request), res
(response), and next
objects in the application's request-response cycle. They can perform operations like modifying the request or response objects, ending the request-response cycle, or calling the next()
function to pass control to the next middleware function in the stack.
Custom Middleware in Express.js
Custom middleware is middleware that you define yourself to handle specific tasks, such as logging, authentication, validation, or anything else that fits your application’s needs.
Structure of Custom Middleware
A custom middleware function typically has this structure:
function customMiddleware(req, res, next) {
// Your logic goes here
next(); // Passes control to the next middleware
}
req
: The HTTP request object.res
: The HTTP response object.next()
: A callback function that is used to move to the next middleware function. If you don't callnext()
, the request will hang and won't proceed to the next stage (e.g., to the route handler or another middleware).
Example: Simple Custom Middleware
const express = require('express');
const app = express();
// Define custom middleware
const customMiddleware = (req, res, next) => {
console.log(`Request Method: ${req.method} | Request URL: ${req.url}`);
next(); // Pass to the next middleware or route handler
};
// Use custom middleware globally
app.use(customMiddleware);
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
In this example:
- The custom middleware logs the request method and URL every time a request is made.
- After logging, it calls
next()
, allowing the request to proceed to the route handler.
Using Custom Middleware for Specific Routes
Instead of applying custom middleware globally, you can apply it to specific routes:
app.get('/about', customMiddleware, (req, res) => {
res.send('About Page');
});
In this case, the middleware only runs when a request is made to /about
.
Practical Use Cases for Custom Middleware
Logging: Log details of incoming requests (e.g., time, method, headers).
const loggerMiddleware = (req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); }; app.use(loggerMiddleware);
Authentication: Check if a user is authenticated before proceeding to protected routes.
const authMiddleware = (req, res, next) => { if (req.headers.authorization) { next(); // User is authenticated } else { res.status(401).send('Unauthorized'); } }; app.get('/dashboard', authMiddleware, (req, res) => { res.send('Welcome to the dashboard'); });
Error Handling: Capture errors and handle them in a centralized way.
const errorHandler = (err, req, res, next) => { console.error(err.stack); res.status(500).send('Something went wrong!'); }; app.use(errorHandler);
Data Validation: Validate request payloads before processing them.
const validateUserInput = (req, res, next) => { const { name, email } = req.body; if (!name || !email) { return res.status(400).send('Name and email are required'); } next(); }; app.post('/register', validateUserInput, (req, res) => { res.send('User registered successfully'); });
Middleware Execution Order
Middleware is executed in the order it is defined. If multiple middleware functions are used, the order in which you declare them matters.
app.use(middleware1);
app.use(middleware2);
app.use(middleware3);
When a request is made, middleware1
will execute first, then middleware2
, and finally middleware3
.
Middleware That Ends the Request-Response Cycle
Not all middleware functions have to call next()
. Middleware can end the request-response cycle by sending a response.
const terminateRequest = (req, res, next) => {
res.send('Request ended here');
};
app.get('/terminate', terminateRequest);
In this case, when a request is made to /terminate
, the request-response cycle will end, and no further middleware or route handlers will be executed.
Summary
- Custom middleware allows you to handle specific tasks such as logging, authentication, validation, etc., in your Express.js application.
- Middleware can be applied globally or to specific routes.
- Middleware functions are called sequentially, and you use the
next()
function to pass control to the next middleware or route handler. - Middleware can also terminate the request-response cycle without calling
next()
.
Custom middleware provides great flexibility in building modular, reusable logic that you can apply to different stages of the request-response lifecycle.