Understanding Express Middleware

Summary: in this tutorial, you will learn about Express middleware and understand how it works.

Introduction to Express Middleware #

In Express, a middleware is a function that has access to the following objects:

  • The Request object (req).
  • The Response object (res).
  • The next middleware function (next).

Technically, an Express application is a series of middleware function calls:

express middleware

The following defines a middleware function:

function fn(req, res, next) {
   // ...
}

Typically, you use middleware to perform the following tasks:

  • Change the request and response objects.
  • Terminate the request-response cycle.
  • Add custom logic to the request/response cycle such as validation, authentication, and authorization.
  • And call the next middleware function.

If you don’t end the request/response cycle in the current middleware function, you must call the next() function to pass control to the next middleware function:

function fn(req, res, next) {
   // ... 
   next();
}

The request will be left hanging if you don’t call the next() function.

To end the request/response cycle in a function, you need to send a response back to the client by using one of the following methods:

MethodMeaning
res.send()Send a response body and end the request/response cycle
res.json()Send a JSON response and end the request/response cycle
res.redirect()Redirect the client to another URL and ends the request/response cycle.
res.end()End the request/response cycle without sending any data.

Express middleware types #

An Express app can have the following types of middleware:

  • Application-level middleware
  • Router-level middleware
  • Built-in middleware
  • Third-party middleware
  • Error-handling middleware

Application-level middleware #

There are two ways to bind an application-level middleware to an instance of the Express app object.

First, pass the middleware function to the app.use() function:

import express from 'express';
const app = express();

// define a middleware
const middleware = (req, res, next) {
    console.log("Execute the middleware");
    next();
}

app.use(middleware);

// ...

Second, pass the middleware function to the app.METHOD() function such as app.get(), app.post(), and so on:

import express from 'express';
const app = express();

// define a middleware
const middleware = (req, res, next) {
    console.log("Execute the middleware");
    next();
}

app.get('/', middleware, (req, res) => {
    // route handler
});

// ...

Notice that route handlers are also middleware functions:

(req, res) => {
    // ...
}

However, a route handler often ends the request/response cycle with a call to res.send() or res.json() method. Therefore, you don’t need to pass the next function and call it inside the router handler.

The following example defines an application middleware that logs the request method and URL in the console window:

import express from 'express';

const PORT = process.env.PORT || 3000;
const app = express();

// define a middleware
const log = (req, res, next) => {
  console.log(`Request: ${req.method} ${req.url}`);
  next();
};

// register the middleware
app.use(log);

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

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

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

How it works.

First, define a middleware function called log that displays the URL and method of the request to the console:

const log = (req, res, next) => {
  console.log(`Request: ${req.method} ${req.url}`);
  next();
};

Second, register the middleware using the app.use() method:

app.use(log);

The log middleware function will execute for every request of all the routes including / and /about.

When you make a GET request to the path / , Express will execute the log middleware function and display the log messages in the console:

Request: GET /

Similarly, if you make a GET request to the /about path, the log middleware function will execute and show the following to the console:

Request: GET /about

Notice that you need to register the middleware before any router handlers. Otherwise, the middleware will not take effect. For example:

import express from 'express';

const PORT = process.env.PORT || 3000;
const app = express();

// define a middleware
const log = (req, res, next) => {
  console.log(`Request: ${req.method} ${req.url}`);
  next();
};

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

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


// register a middleware
app.use(log); // DO NOT RUN for the / and /about routes


app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

In this example, the middleware registration is after all the router handlers. When you request the / and /about routes, the middleware will not take any effects.

To execute the log middleware function for only the /about method, you can pass it to the corresponding route handler as follows:

// ...

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

// ...

In this case, the log middleware function only executes for the GET method of route /about.

Router-level middleware #

The router-level middleware is like an application-level middleware except it is bound to the Router instance instead of the Express app instance.

We’ll discuss the router-level middleware in the Express router tutorial.

Built-in middleware #

Express provides some built-in middleware functions:

  • express.json middleware allows you to parse the requests with the JSON body. (available since Express 4.16.0 and later).
  • express.static middleware serves static assets such as HTML files, images, etc.
  • express.urlencoded parses the requests with a URL-encoded body.

Third-party middleware #

There are many third-party middleware available. The third-party middleware allows you to enhance the functionality of the Express apps.

Third-party middleware requires installation via package managers such as npm. We’ll introduce you to some of the most commonly used third-party middleware in upcoming tutorials.

Error-handling middleware #

Unlike a regular middleware, an error-handling middleware takes four parameters instead of three:

function fn(err, req, res, next) {
   // ...
}

The first argument of an error-handling middleware is an error object. We’ll discuss the error-handling middleware in the error-handling tutorial.

Summary #

  • A middleware is a function that has access to the request, response, and the next middleware function.
  • Use the app.use() function to register a middleware.
  • Register the middleware before particular router handlers to apply the middleware to these route handlers.

Was this helpful?