Table of contents
1.
Introduction
2.
What is Middleware in Node.js?
3.
Types of Middleware in Node.js
4.
Creating a Middleware
5.
Middleware Syntax
6.
How Does Node.js Middleware Pattern Work?
7.
What Happens When the Request Reaches the Last Middleware in the Chain?
8.
What is next()?
9.
Some Commonly Used Middleware
10.
Middleware Chaining
11.
Frequently Asked Questions
11.1.
What is middleware in Node.js? 
11.2.
What happens if `` is not called? 
11.3.
Can middleware be reused across multiple routes? 
12.
Conclusion
Last Updated: Jan 3, 2025
Easy

Middleware in Node.js

Author Pallavi singh
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Middleware is a fundamental concept in Node.js that plays a crucial role in handling requests and responses in web applications. It acts as a bridge between the server and the client, processing requests, performing specific tasks, and determining how the application should respond. In Node.js, Middleware is a function that runs during the process of handling a request. It can modify the request or response, end the request-response cycle, or pass control to the next function. Middleware is useful for tasks like checking if a user is logged in, handling errors, or logging information.

Middleware in Node.js

In this article, we will discuss what middleware is, how it works, and some common use cases, complete with examples and code snippets.

What is Middleware in Node.js?

Middleware refers to functions that execute during the lifecycle of an HTTP request. These functions have access to the req (request) and res (response) objects, as well as a next() function that allows you to pass control to the next middleware in the stack. Middleware is used to perform operations like:

  • Validating or authenticating user requests.
     
  • Parsing incoming request bodies.
     
  • Serving static files.
     
  • Logging requests and responses.
     

In simple terms, middleware helps manage and streamline the flow of data in a Node.js application.

Types of Middleware in Node.js

Node.js middleware can be categorized into several types based on their functionality & scope. Let’s look at the main types of middleware we usually see:

1. Application-level middleware: This type of middleware is bound to the app object using the app.use() or app.METHOD() functions, where METHOD is an HTTP method like GET, POST, PUT, etc. Application-level middleware is executed for every request to the application.
 

2. Router-level middleware: Router-level middleware works similarly to application-level middleware but is bound to an instance of express.Router(). It's used to define middleware for specific routes or a group of routes.
 

3. Error-handling middleware: This middleware has a special function signature with four arguments: (err, req, res, next). It's used to handle errors that occur during the request-response cycle. Error-handling middleware should be placed last in the middleware stack.
 

4. Built-in middleware: Express.js provides built-in middleware functions like express.static() for serving static files & express.json() for parsing JSON request bodies. These middleware functions are ready to use without requiring additional installation.
 

5. Third-party middleware: There are numerous third-party middleware packages available in the Node.js ecosystem. These packages provide additional functionality & can be easily integrated into your application. Examples include body-parser for parsing request bodies, morgan for logging, & helmet for security enhancements.

Creating a Middleware

Now that you understand the types and syntax of middleware, let's dive into creating custom middleware functions. Here's an example of creating a simple middleware that logs the request method and URL:

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

// Custom middleware function
const loggerMiddleware = (req, res, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next();
};

// Register the middleware
app.use(loggerMiddleware);

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

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


In this example:

1. We create a custom middleware function called `loggerMiddleware`. It takes the `req`, `res`, and `next` parameters.
 

2. Inside the middleware function, we log the current timestamp, request method, and URL using `console.log()`.
 

3. After logging, we call `next()` to pass control to the next middleware or route handler.
 

4. We register the middleware using `app.use(loggerMiddleware)`. This applies the middleware to all routes in the application.
 

5. We define a route handler for the root URL ('/') that sends a simple "Hello, World!" response.
 

6. Finally, we start the server and listen on port 3000.


When a request is made to the server, the `loggerMiddleware` will be executed before the route handler. It will log the request details and then pass control to the next middleware or route handler.
 

You can create more complex middleware functions that perform tasks like authentication, error handling, request validation, or any other custom logic your application requires.
 

Remember, the order in which you register middleware is important. Middleware functions are executed in the order they are registered using `app.use()` or `app.METHOD()`.

Middleware Syntax

Middleware functions in Node.js follow a specific syntax. They are functions that have access to the request object (req), the response object (res), & the next middleware function in the application's request-response cycle, commonly denoted as next.

The syntax of a middleware function is:

function middlewareFunction(req, res, next) {
  // Perform operations
  // Modify req or res objects if needed
  // Call next() to pass control to the next middleware
  next();
}


In this syntax: 

- `req`: The request object represents the HTTP request. It contains information such as headers, URL parameters, query strings, & request body.

- `res`: The response object represents the HTTP response that will be sent back to the client. You can use methods like `res.send()`, `res.json()`, `res.status()`, etc., to modify the response.

- `next`: The `next` function is a callback that passes control to the next middleware function in the stack. Calling `next()` signals that the current middleware has completed its task & the request should proceed to the next middleware or route handler.


It's important to make sure that if a middleware function doesn't end the request-response cycle (e.g., by sending a response), it must call `next()` to pass control to the next middleware. Otherwise, the request will be left hanging, & the client won't receive a response.

Note: You can chain multiple middleware functions together by calling `next()` in each middleware. This allows you to perform a series of operations on the request or response before the final route handler sends the response back to the client.

How Does Node.js Middleware Pattern Work?

The middleware pattern in Node.js is based on a series of chained functions. Each middleware function processes the request and can decide to:

  1. Modify the req or res objects.
     
  2. Perform specific tasks, such as authentication or logging.
     
  3. End the request-response cycle.
     
  4. Pass control to the next middleware in the stack by calling next().
     

Here is a simple example:

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

// A simple middleware function
app.use((req, res, next) => {
    console.log(`Request Method: ${req.method}, Request URL: ${req.url}`);
    next(); // Pass control to the next middleware
});

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


// Starting the server
app.listen(3000, () => {
    console.log('Server is running on port 3000');
});

 

Output:

  • If you visit http://localhost:3000, the server logs the request method and URL before responding with "Hello, Middleware!"

What Happens When the Request Reaches the Last Middleware in the Chain?

When a request reaches the last middleware in the chain, two scenarios can occur:

The Response is Sent: The middleware ends the request-response cycle using methods like res.send(), res.json(), or res.end(). This prevents further middleware from executing.

Example:

app.use((req, res) => {
    res.send('This is the last middleware!');
});
  1. No Response Sent: If the middleware neither sends a response nor calls next(), the request hangs indefinitely, leading to a timeout error.

What is next()?

The next() function is an integral part of middleware in Node.js. It allows middleware to pass control to the next middleware in the stack. Without calling next(), the request-response cycle cannot proceed to subsequent middleware or route handlers.

Example

app.use((req, res, next) => {
    console.log('First middleware');
    next(); // Moves to the next middleware
});

app.use((req, res, next) => {
    console.log('Second middleware');
    next(); // Moves to the final route handler
});

app.get('/', (req, res) => {
    res.send('Hello from the final route!');
});


Output:

  • Console Logs:
    • "First middleware"
       
    • "Second middleware"
       
  • Browser Response: "Hello from the final route!"

Some Commonly Used Middleware

Node.js applications often rely on both built-in and third-party middleware. Here are some commonly used middleware:

1.Body Parser- Parses incoming request bodies in JSON or URL-encoded format.

const bodyParser = require('body-parser');
app.use(bodyParser.json()); // Parses JSON data
app.use(bodyParser.urlencoded({ extended: true })); // Parses URL-encoded data


2.Static File Serving- Serves static files like HTML, CSS, and JavaScript.

app.use(express.static('public'));


3.Logger- Logs details of incoming requests.

const morgan = require('morgan');
app.use(morgan('dev'));


4. Authentication- Verifies user credentials before granting access to resources.

app.use((req, res, next) => {
    const isAuthenticated = checkAuth(req.headers.authorization);
    if (!isAuthenticated) {
        return res.status(401).send('Unauthorized');
    }
    next();
});
function checkAuth(token) {
    // Logic for verifying token
    return token === 'valid-token';
}

Middleware Chaining

Middleware chaining involves connecting multiple middleware functions in a sequence to process requests. This is achieved by calling next() within each middleware function.

Example of middleware chaining:

app.use((req, res, next) => {
    console.log('Step 1: Logging request');
    next();
});
app.use((req, res, next) => {
    console.log('Step 2: Authenticating user');
    next();
});
app.use((req, res) => {
    console.log('Step 3: Sending response');
    res.send('Request processed successfully!');
});


Output:

  • Console Logs:
    • "Step 1: Logging request"
       
    • "Step 2: Authenticating user"
       
    • "Step 3: Sending response"
       
  • Browser Response: "Request processed successfully!"

Frequently Asked Questions

What is middleware in Node.js? 

Middleware are functions that handle requests and responses during an application's lifecycle. They can modify requests, process data, and pass control to the next middleware using next().

What happens if `` is not called? 

If next() is not called and no response is sent, the request will hang, leading to a timeout error.

Can middleware be reused across multiple routes? 

Yes, middleware can be applied to specific routes or globally to all routes using app.use() or route-specific methods.

Conclusion

Middleware is an essential component of Node.js applications, enabling developers to manage the request-response lifecycle effectively. It provides flexibility to perform tasks like logging, authentication, and error handling, ensuring a smooth workflow.

You can also check out our other blogs on Code360.

Live masterclass