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 call next(), 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

  1. 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);
  2. 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'); });
  3. 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);
  4. 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.