Easy Guide To Understanding ExpressJS Middleware For Rapid App Development

Emmanuel Fordjour Kumah - Jan 20 '23 - - Dev Community

An Express app is largely composed of middleware function calls that enable developers to quickly build web applications.

In this article, we will learn what middleware is, the importance of middleware, and how to write your own middleware for ExpressJS applications.

By the end of the article, you should know :

  • What middleware is

  • The Importance of middleware

  • The built-in Express middleware functions

  • How to create and use middleware function in our Express app.

Prerequisites:

Before getting started, you should have knowledge on:

Introduction to Middleware

Whenever an Express app receives a request, it needs to send back a response. A middleware component sits between a request and a response.

It is those methods/functions/operations that are called between processing the request and sending the response in an Express app.

Middleware executes whenever the server receives a request and has access to the request object (req), the response object(res), and a next() function.

How middleware works

Consider an analogy, where you are a property owner with tons of apartments to let out to tenants. However, you are selective about the type of tenant that occupies the apartment. You want tenants with specific social classes and ethnicities.

To schedule a viewing, potential clients must call you.

To avoid the headache of screening all potential tenants, you hire a real estate agent to oversee these activities. Whenever a potential tenant requests an apartment, the agent will schedule an initial meeting with the client. The agent screens a client to determine if he meets your criteria.

The agent informs you to allow the client to view the apartment once he meets all the requirements. You then pay the agent a commission for his service.

In this analogy:

  • The tenant represents the HTTP request performed by the client

  • The real estate agent represents the middleware

  • The property owner represents the server

The agent (middleware) ensures any request by a client to access an apartment meets the criteria determined by the property owner ( server).

In effect, the middleware (i.e the real estate agent) sits between the client (i.e tenant) and the server(i.e property owner ) and determines the next action to take.

Should the client not meet the criteria, the middleware has the right to terminate the request. In effect, ending the request-response cycle.

Importance of middleware

Middleware functions can perform the following tasks:

  • Add logging and authentication functionality to our application

  • Can be used to manipulate requests

  • Make changes to the request and response objects

  • Call the next middleware function in the stack

  • Can process request objects multiple times before the server works on the request.

  • It improves client-side rendering performance.

Types of middleware functions

An Express app can use the following types of middleware:

  • Application-level middleware: a type of middleware that generally runs throughout the whole application

  • Router-level middleware: router-level middleware work just like application-level middleware except they are bound to an instance of express. Router()

  • Error-handling middleware: the purpose of error-handling middleware is to detect and capture multiple error conditions and take corrective actions to recover from or gracefully fail in the face of those errors.

  • Built-in middleware: middleware built into the Express application.

  • Third-party middleware: third-party packages available that provide the functionality that our app requires.

We will discuss these types as we progress.

Setting up the ExpressJS development environment

To demonstrate how middleware works, we will set up a basic ExpressJS application.

Follow these steps to set up an Express app

  • Open your favorite code editor and create a project directory.

  • Install the package.json file

  • Install the express library in the directory

The following commands illustrate the above:

mkdir middleware-tuts
cd middleware-tuts
npm init -y
npm install express
Enter fullscreen mode Exit fullscreen mode

Create an index.js file and add the code snippets below to create a basic express application.

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

app.get("/", (req, res) => {
  res.send(`<h1>Home</h1>`);
});

app.listen(5000, () => {
  console.log("App has started and listening on port 5000");
});
Enter fullscreen mode Exit fullscreen mode

You can also install nodemon using the command below. This will automatically restart the server whenever it detects changes to the file.

npm install nodemon
Enter fullscreen mode Exit fullscreen mode

Modify the scripts in the package.json file to the following:

 "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
Enter fullscreen mode Exit fullscreen mode

Start the application using the command below:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Congratulations, you have created an Express app. Visit http://localhost:5000 to view your app.

Creating the middleware in an Express app

Because middleware is a function, we can use any preferred function declaration. We pass the function three parameters req, res and next.

The syntax is as below:

const middleWareName = (req,res,next)=> {
//logic here
next() // call the next middleware
 }
Enter fullscreen mode Exit fullscreen mode

As mentioned, one of the main tasks of middleware is to manipulate requests.

In the example below, we create a basic logger middleware that logs a message whenever the client requests a resource

//create the middleware
const logger = (req, res, next) => {
  console.log(`Hello, you've hit our server`);
  next();
};
Enter fullscreen mode Exit fullscreen mode

Understanding the middleware parameters

Middleware accepts three parameters req and res objects and the next function.

Manipulating the request object

  • The req is passed as a parameter during the function declaration.

  • Because middleware enables us to manipulate requests, in the body of the function, we can access all the available methods in the req object.

In the example below, whenever we receive a request, we log the HTTP method used and a message

//declare the logger middleware
const logger = (req, res, next) => {
  console.log(req.method); // logs the http request

  //log the below whenever the client reqest for a resouce
  console.log(`Hello you've hit our server`);
  next();
};
Enter fullscreen mode Exit fullscreen mode

If the client is requesting data, the output will be:

GET
Hello you've hit our server
Enter fullscreen mode Exit fullscreen mode

Manipulating the response object

The middleware can also manipulate the res object.

In the example below, we use the res.send() method to send back a response to the client.

//declare a logger middleware
const logger = (req, res, next) => {
  console.log(req.method); //logs the http reqest method
  //send a response to the client
  res.send(`<h1>Middleware responded</h1>`);
};
Enter fullscreen mode Exit fullscreen mode

Whenever you use the res object in the function's body, it will not allow any request to go past it.

In effect, any other logic we may have implemented in the app will not be accessed.

Accessing the next() function.

The next() is a function that enables us to invoke the next middleware in the middleware stack.

Whenever the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware else the request will be left hanging (evidenced by the browser always reloading).

Below is how we pass the control to the next middleware

const logger = (req, res, next) => {
  ... // all other logic here
  next() // call the next middleware
};
Enter fullscreen mode Exit fullscreen mode

Using the middleware

Until now, we have learned what middleware is and the functions of all the parameters.

Let's now see how we use declared middleware.

The syntax below illustrates how to use a middleware function

app.use([path,] callback [, callback...])
Enter fullscreen mode Exit fullscreen mode
  • The app.use() connects the declared middleware function(s) to our application at the specified path.

  • The path represents the route for which the middleware function is invoked.

  • The callback represents a middleware function, a series of middleware functions (separated by commas), or an array of middleware functions that will be executed.

The code snippet below is how we will use the logger middleware function

//using the logger middleware
app.use("/", logger);
Enter fullscreen mode Exit fullscreen mode

In the example above, whenever the client visits the home route (/) the logger middleware is executed.

Middleware staged without a path , for instance, app.use(middleware) will be executed for every request to the app.

Middleware order of execution

Middleware is executed in the order in which they are added.

Consider the example below:

app.use(middleware1)
app.use(middleware2)
Enter fullscreen mode Exit fullscreen mode

app.use(middleware1) will be executed before app.use(middleware2)

Using multiple middlewares

In our app.use(), we can declare an array of middleware functions that will be executed depending on the route requested.

In the code below, we declare the querymiddleware in addition to the logger middleware.

//declare the logger middleware
const logger = (req, res, next) => {
  console.log(req.method); // logs the http request

  //log the below whenever the client reqest for a resouce
  console.log(`Hello you've hit our server`);
  next();
};

//query middeleware
const query = (req, res, next) => {
  console.log(req.query)
  next();
};
Enter fullscreen mode Exit fullscreen mode

The querymiddleware also sits between the request-response cycle and can manipulate requests.

With this example, the req.query object becomes populated with the query strings. These query strings are key-value pairs and start after the question mark in any URL.

For instance http://localhost:3000/dashboard/user?name=emmanuel&age=25

The output will be :

{ name: 'emmanuel', age: '25' }
Enter fullscreen mode Exit fullscreen mode

Let's now use the declared middleware. The syntax below indicates how to use a series of middleware in our application

//array of middlewares
app.use([mddleware1, middleware2]);
Enter fullscreen mode Exit fullscreen mode

We can use the logger and querymiddlewares like the below:

//use the middlewares
app.use([logger, query]);
Enter fullscreen mode Exit fullscreen mode

Registering the path to execute the middleware

Depending on the http method use, we can specify the path that all the declared middleware will be executed

The syntax is as below:

app.method(path, middleware, (req, res) => {
  //do something here
});
Enter fullscreen mode Exit fullscreen mode

Assuming, we want the querymiddleware to be executed only when the clients request a resource on the path : dashboard/user , we define it as below:

app.get("/dashboard/user", query , (req, res) => {
  //do something here
});
Enter fullscreen mode Exit fullscreen mode

In effect, the querymiddleware will only be invoked when the path specified in our app matches that of the client whiles the logger middleware executes when the client requests a resource on the root /.

The complete code for declaring and using middleware is as below:

const express = require("express");
const app = express();
const port = 3000;

//declare the logger middleware
const logger = (req, res, next) => {
  console.log(req.method); // logs the http request

  //log the below whenever the client reqest for a resouce
  console.log(`Hello you've hit our server`);
  next();
};

//declare the query middeleware
const query = (req, res, next) => {
  console.log(req.query);
  next();
};

//use the middlewares
app.use([logger, query]);

//logger middleware get executed here
app.get("/", (req, res) => {
  res.send(`<h1>Hello World !</h1>`);
});

//use the query middleware
app.get("/dashboard/user", query, (req, res) => {
  // access the name key of  the query string
  const { name } = req.query;

  res.send(`<h1>Welcome ${name}</h1>`);
});

app.listen(port, () => {
  console.log(`Example app is listening on port ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Using Built-in middleware

Express has built-in middlewares that enable us to perform tasks.

Examples of these are:

  • express.static

  • express.json

  • express.urlencoded

  • express.raw

  • express.text

Using the express.static middleware

The express.static built-in middleware enables us to render static files such as images, CSS, and JavaScript files on the client-side

Unlike sending a file using the res.sendFile() which permits serving only single files, the express.static() allows all files in a specified folder to be available to the public.

The syntax is as below

express.static(root, [options])
Enter fullscreen mode Exit fullscreen mode

The root represents the root directory from which to serve static assets. If we want to render web pages, the directory should contain an index.html or index.js file which serves as the home page.

Assuming we created a public folder containing all the required files in our project directory, we can serve these files using the code snippet below.

express.static('public') // built-in middleware
Enter fullscreen mode Exit fullscreen mode

We can use this built-in middleware as below:

app.use(express.static('public'))
Enter fullscreen mode Exit fullscreen mode

Using express.json middleware

Express provides a middleware to handle incoming data in the request's body.

The express.json() is a built-in middleware that enables us to recognize incoming request objects as JSON.

It enables us to send data to the server.

The syntax is as below:

express.json([options])
Enter fullscreen mode Exit fullscreen mode

We can use this built-in middleware as

// parse json
app.use(express.json())
Enter fullscreen mode Exit fullscreen mode

Using the express.urlencoded middleware

The express.urlencoded() enable us to identify incoming request objects as strings or arrays.

This method is called using the code:

app.use(express.urlencoded())
Enter fullscreen mode Exit fullscreen mode

Third-party middleware

There are many third-party middleware packages available that provide the functionality that our app requires.

Below are some examples:

  • morgan: an HTTP request logger middleware

  • helmet: a middleware that improves the security of your application.

  • compression: a middleware that attempts to compress response bodies for all of the responses.

Summary

  • Middlewares are methods/functions/operations that are called between processing the request and sending the response in an Express app.

  • It sits between the request and response cycles on an Express app

  • It can be used to manipulate requests

  • It can also make changes to the request and response objects

This article is the day 7 post on the 30-day challenge I am undertaking to learn backend development. Please feel free, to drop some insights, comment, or share the post to your networks

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .