Custom Middlewares

Middleware functions are at the heart of Express.js. They are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. This capability allows for building robust and scalable applications by enabling functionalities like logging, authentication, and error handling. In this blog, we'll explore how to create custom middlewares in Express.js, step by step.

Table of Contents

  1. Introduction to Middleware

  2. Setting Up Express Application

  3. Creating Custom Middleware

  4. Using Middleware in Routes

  5. Chaining Multiple Middleware Functions

  6. Error-Handling Middleware

  7. Conclusion

Introduction to Middleware

Middleware functions can perform various tasks such as:

  • Executing any code.

  • Modifying the request and response objects.

  • Ending the request-response cycle.

  • Calling the next middleware in the stack.

If the current middleware does not end the request-response cycle, it must call next() to pass control to the next middleware.

Setting Up Express Application

Before diving into creating custom middleware, let’s set up a basic Express application.

Step 1: Install Express

First, ensure you have Node.js installed. Then, create a new project and install Express:

mkdir express-middleware-demo
cd express-middleware-demo
npm init -y
npm install express

Step 2: Create the Server File

Create a file named app.js:

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

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Run the application:

node app.js

Your server should be running at http://localhost:3000.

Creating Custom Middleware

Let's create a simple custom middleware that logs the request method and URL.

Step 1: Define the Middleware Function

Create a directory named middleware and a file named logger.js:

// middleware/logger.js
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next(); // Pass control to the next middleware
};

module.exports = logger;

Step 2: Use the Middleware in the Application

In app.js, require and use the logger middleware:

const express = require('express');
const app = express();
const logger = require('./middleware/logger');
const PORT = 3000;

app.use(logger); // Use the custom middleware

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Now, every request to the server will be logged to the console.

Using Middleware in Routes

Middleware can also be applied to specific routes.

Step 1: Create Route-Specific Middleware

Modify app.js:

const express = require('express');
const app = express();
const logger = require('./middleware/logger');
const PORT = 3000;

// Global middleware
app.use(logger);

// Route-specific middleware
const specificLogger = (req, res, next) => {
  console.log(`Specific Logger: ${req.method} ${req.url}`);
  next();
};

app.get('/', (req, res) => {
  res.send('Hello World!');
});

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

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Only requests to /about will be logged by specificLogger.

Chaining Multiple Middleware Functions

You can chain multiple middleware functions for a route.

Step 1: Create Additional Middleware

Create another middleware file auth.js:

// middleware/auth.js
const auth = (req, res, next) => {
  const authenticated = true; // Simplified authentication check
  if (authenticated) {
    next();
  } else {
    res.status(403).send('Forbidden');
  }
};

module.exports = auth;

Step 2: Apply Multiple Middleware Functions

Modify app.js:

const express = require('express');
const app = express();
const logger = require('./middleware/logger');
const auth = require('./middleware/auth');
const PORT = 3000;

app.use(logger);

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.get('/dashboard', auth, (req, res) => {
  res.send('Dashboard');
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Requests to /dashboard will pass through both logger and auth middleware.

Error-Handling Middleware

Error-handling middleware is defined in the same way as other middleware, but with four arguments instead of three: (err, req, res, next).

Step 1: Create Error-Handling Middleware

Create a file errorHandler.js:

// middleware/errorHandler.js
const errorHandler = (err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
};

module.exports = errorHandler;

Step 2: Use Error-Handling Middleware

Modify app.js to use the error handler:

const express = require('express');
const app = express();
const logger = require('./middleware/logger');
const auth = require('./middleware/auth');
const errorHandler = require('./middleware/errorHandler');
const PORT = 3000;

app.use(logger);

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.get('/dashboard', auth, (req, res) => {
  res.send('Dashboard');
});

// Intentionally cause an error to test the error handler
app.get('/error', (req, res) => {
  throw new Error('Test Error');
});

// Error-handling middleware should be defined last
app.use(errorHandler);

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Requests to /error will trigger the error handler.

Conclusion

Custom middlewares in Express.js are powerful tools for adding functionality to your applications. By creating and using custom middleware, you can handle logging, authentication, error handling, and more in a modular and reusable way. Middleware functions provide a clean and organized approach to manage the request-response cycle, making your codebase more maintainable and scalable.

Last updated