Custom middleware lets you add specific functionality to your Express.js applications, such as logging, authentication, or modifying requests. Unlike built-in or third-party middleware, custom middleware gives you full control over how requests are processed and handled before reaching your routes.



What is Custom Middleware?

Custom middleware is a user-defined function (like a checkpoint) that each request passes through before reaching your route. With custom middleware, you can intercept requests, run your code, and then decide what to do next: let the request continue to the next middleware or route handler, or stop it and send back the response.

A custom middleware typically has three parameters:

  • req: The request object.
  • res: The response object.
  • next: A function to pass control to the next middleware in the stack.

Here's a quick look at what a custom middleware function looks like:

Example:

function customMiddleware(req, res, next) {
  // Your custom logic here
  console.log('Custom middleware running');
  next(); // Move to the next middleware or route
}

The important part is the next() function. If you don't call it, your request won't go any further, which could leave your users staring at a blank screen!

Adding Custom Middleware to Your App

You can easily add custom middleware to your Express app using app.use() or directly attach it to specific routes. Let's start simple.

Logging Requests

Let's create a custom middleware that logs the details of incoming requests.

Example:

const express = require('express');
const app = express();

// Custom middleware to log requests
function logRequests(req, res, next) {
  console.log(`${req.method} request to ${req.url} at ${new Date().toLocaleTimeString()}`);
  next(); // Proceed to the next middleware or route
}

app.use(logRequests); // Apply it globally

app.get('/', (req, res) => {
  res.send('Home Page');
});

app.get('/about', (req, res) => {
  res.send('About Page');
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Now, every request will log the method, URL, and timestamp to the console. This can be super useful when debugging!

Applying Middleware on Particular Routes

Not every route needs the same treatment. If you only want middleware for specific routes, you can pass it directly into the route definition.

Example:

app.get('/protected', logRequests, (req, res) => {
  res.send('Protected Page');
});

Now, the logRequests middleware will only run when the /protected route is hit.

Simple Authentication Middleware

Imagine you're building a basic authentication system where requests need to include a secret token. Let's write a middleware for that.

Example:

// Custom middleware for token-based authentication
function authenticate(req, res, next) {
  const token = req.headers['authorization'];
  
  if (token === 'mysecrettoken') {
    next(); // Token is valid, continue
  } else {
    res.status(401).send('Unauthorized');
  }
}

// Apply to a protected route
app.get('/dashboard', authenticate, (req, res) => {
  res.send('Welcome to the dashboard');
});

This middleware checks if the request includes a valid token. If not, it sends back a 401 Unauthorized response. You can build much more complex authentication mechanisms on top of this simple setup.

Chaining Multiple Middleware Functions

What if you have several tasks to handle? You can chain middleware functions so that they run one after the other.

Example:

function first(req, res, next) {
  console.log('First middleware');
  next(); // Move to the next middleware
}

function second(req, res, next) {
  console.log('Second middleware');
  next(); // Move to the next middleware
}

app.get('/multi', first, second, (req, res) => {
  res.send('Multiple middleware executed');
});

In this example, both middleware functions will run in sequence before reaching the final route handler.

Validating Data with Middleware

Let's say you're accepting form data and need to ensure certain fields are present. Middleware can handle that easily.

Example:

// Custom middleware for validating form data
function validateFormData(req, res, next) {
  const { name, email } = req.body;

  if (!name || !email) {
    return res.status(400).send('Bad Request: Name and email are required');
  }

  next(); // Data is valid, move to the next step
}

// Apply to the form submission route
app.post('/submit-form', validateFormData, (req, res) => {
  res.send('Form submitted successfully');
});

Now, every time a form is submitted to /submit-form, the middleware will check if name and email are provided. If not, it sends back a 400 Bad Request response.

Handling Errors in Custom Middleware

You can also create middleware specifically to handle errors. Error-handling middleware has a slightly different signature—it uses four arguments: err, req, res, and next.

Example:

function errorHandler(err, req, res, next) {
  console.error(err.message);
  res.status(500).send('Something went wrong!');
}

// Apply error-handling middleware
app.use(errorHandler);

Any time an error occurs in your app, Express will pass it to this error-handling middleware, and it will send a 500 Internal Server Error response.

Wrapping Up

Custom middleware in Express.js is incredibly versatile. You can add logging, authentication, data validation, and much more, all in a modular way. By breaking down your app's logic into middleware functions, you keep your code clean, reusable, and easy to maintain.

Here's a quick recap:

  • Middleware functions sit between the request and response, allowing you to intercept, modify, or block requests.
  • You can apply middleware globally or to specific routes.
  • Use middleware for logging, authentication, validation, and error handling.
  • Always remember to call next() unless you want to terminate the request.

Once you get comfortable with middleware, you'll find endless possibilities to customize your app's behavior!



Found This Page Useful? Share It!
Get the Latest Tutorials and Updates
Join us on Telegram