DEV Community

Cover image for What is Middleware in Express and How It Works
Abhishek sahni
Abhishek sahni

Posted on • Originally published at jsoperators.hashnode.dev

What is Middleware in Express and How It Works

Express is a widely used framework of Node.js for backend development. Express provides robust middleware support, which helps developers write reusable and maintainable code.

In this blog, we are diving deep into middleware. By the end of this blog, you will understand all the important concepts of middleware.

What is Middleware?

Middleware is a function that runs during the request-response cycle, sitting between the incoming request and the final route handler.

Every middleware function accepts three arguments:

  • req (request)

  • res (response)

  • next

req (Request)

The request object contains incoming data sent by the client.

res (Response)

The response object is used by the server to send data back to the client.

next()

The next() function passes control to the next middleware function. If there is no next middleware, the request moves to the final route handler.

Important Note

A middleware must either end the request-response cycle by sending a response using res.send() (or similar methods) or pass control using next(). Otherwise, the client request will remain hanging between the request and response cycle.

Types of Middleware :

  • Application-level middleware :

    Application-level middleware is bound to the Express application instance using app.use() and app.METHOD() functions.

    It runs for every incoming request to the server if a specific path is not provided.

    The execution of middleware follows a top-to-bottom order. Middleware declared at the top runs first, followed by the next middleware in sequence.

    For example, if multiple middleware functions are registered using app.use(), Express executes them one by one in the same order they are written in the code.

    It has full access to the req (request) object, res (response) object, and the next() function throughout the request-response cycle

    const express = require('express');
    const app = express();
    
    // 1. Without a mount path: Executes for every request
    app.use((req, res, next) => {
      console.log('Time:', Date.now());
      next(); // Pass control to the next function
    });
    
    // 2. With a mount path: Executes for any request to /user/:id
    app.use('/user/:id', (req, res, next) => {
      console.log('Request Type:', req.method);
      next();
    });
    
    // 3. With a method: Executes only for GET requests to /user/:id
    app.get('/user/:id', (req, res, next) => {
      res.send('User Page');
    });
    
  • Router-level middleware :

    Router-level middleware is bound to the instance of router not in main application instance which allow use to apply middleware function to a specific router making our code more modular.

    Router-level middleware runs for a specific request that match the path mount to the middleware . It not runs for every request.

    const express = require('express');
    const router = express.Router();
    
    // This middleware is executed for every request to this router
    router.use((req, res, next) => {
      console.log('Router-level request received at:', Date.now());
      next();
    });
    
    // Define routes within this router
    router.get('/profile', (req, res) => {
      res.send('User Profile');
    });
    
    // Mount the router on a path in the main app
    // Only requests starting with /user will trigger the router's middleware
    const app = express();
    app.use('/user', router);
    
  • Built-in :

    Built-in middleware is bound to the instance of app. They are build in middleware of express we don't need the write logic from scratch.

    The primary built-in middleware functions are:

  • express.json(): Parses incoming requests with JSON payloads. It makes the parsed data available under req.body.

  • express.urlencoded({ extended: true }): express.urlencoded() is a built-in middleware in Express.js used to parse incoming form data sent from the client.

    When user submit HTML form data is usually send in this format :

    application/x-www-form-urlencoded
    

This middleware converts that form data into a JavaScript object and stores it inside req.body.

Example :

app.use(express.urlencoded({ extended: true }));
Enter fullscreen mode Exit fullscreen mode
  • express.static(): Serves static files such as images, CSS, and JavaScript from a specified directory (e.g., app.use(express.static('public'))).

  • express.text(): Parses incoming request payloads into a string.

Error-handling : Error handling is a specialized middleware used to catch and process errors in an Express application.

To create an error-handling middleware, you must define a function with four parameters:

(err, req, res, next)
Enter fullscreen mode Exit fullscreen mode

err : Contains information about the error

Code example :

app.use((err, req, res, next) => {
  console.error(err.message);

  res.status(500).send("Something went wrong");
});
Enter fullscreen mode Exit fullscreen mode

Now we understand the middleware with their types :

Let's dive into the execution order of middleware :

In express middleware run sequently in the order as they defined in code. When a request received express passes it through the middleware stack one by one until the response is send res.send() or stack become empty.

Execution Rules :

Definition Order : The order of app.use() and app.METHOD() (like app.get or app.post) calls determines the execution sequence.

next() function : Function: To move to the subsequent middleware, you must call next(). If you don't call it and don't send a response, the request will hang.

Terminating the Cycle: If a middleware sends a response (e.g., res.send() or res.json()), the request-response cycle ends, and subsequent middleware in the stack will not execute.

Real work example of middleware :

One of the real world use case of middleware is authentication used in real world applications like Instagram , Amazon etc.

Suppose user try to access dashboard

middleware checks :

  • is token valid

  • user is logged in or not

  • does the user have permission to access the dashboard

    This checking logic written inside middleware.

function authMiddleware(req, res, next) {
  const isLoggedIn = true;

  if (!isLoggedIn) {
    return res.status(401).send("Please login first");
  }

  next();
}

app.get("/dashboard", authMiddleware, (req, res) => {
  res.send("Welcome to your dashboard");
});
Enter fullscreen mode Exit fullscreen mode

How it works :

Client Request
      ↓
Authentication Middleware
      ↓
Route Handler
      ↓
Response
Enter fullscreen mode Exit fullscreen mode

if user logged in middleware call next() and move the route handler otherwise middleware return response.

Summary : In this blog, we learned that middleware works like a middleman between the request and response cycle. We explored different types of middleware and understood how middleware execution order works in Express.

Top comments (0)