Express Middlewares

In the context of an application lifecycle, middlewares are defined as special functions that have access to the request (req) and response (res) object along with the next() function located in the pipeline.

  1. app.use((req, res, next) => {  
  2.     console.log(req.url); // prints the request url to the console  
  3.     next();  
  4. });  

The code snippet above depicts a basic way of defining a middleware function in an express application and loading it in the request handling pipeline.

Having put aside the obligation of giving a fancy definition, first up, let's actually get down into what exactly are these middleware functions used for and what are the different ways in which we can inject them in an express application lifecycle.

The code provided below demonstrates the setting up of a basic Express application.

We'll go through it line by line and try to understand how and where the middleware functions fit into the overall scheme of things.

  1. const express = require("express");  
  2. const app = express();  
  3. const port = process.env.PORT || 3000;  
  4. app.use(express.static(__dirname + "/public"));  
  5. -- in -built express middleware used  
  6. for telling the application from where to serve static files  
  7. app.use((req, res, next) => {  
  8.     if (req.url.indexOf("books") > -1) {  
  9.         res.status(401).send();  
  10.         --exits the pipeline and returns the response back to the browser  
  11.     } else {  
  12.         console.log(req.url);  
  13.         next();  
  14.         -- //calls the next middleware/request handler in the pipeline  
  15.     }  
  16. });  
  17. app.use((req, res, next) => {  
  18.     console.log(`${req.url} route hit at ${new Date().toLocaleDateString()}`);  
  19.     next();  
  20.     -- //calls the next middleware/request handler in the pipeline  
  21. });  
  22. app.get("/", (req, res, next) => { //request handler for the "/" route  
  23.     res.send("root request route..."); //sends the response back to the browser  
  24.     next();  
  25.     --calls the final middleware in the pipeline  
  26. });  
  27. app.use("/", (req, res) => {  
  28.     console.log("From the Terminal middleware...");  
  29.     console.log("Doing the post request handling cleanup...");  
  30. });  
  31. app.get("/books", (req, res) => {  
  32.     res.send("request route for books");  
  33. });  
  34. app.listen(port, () => {  
  35.     console.log(`The server is listening on port ${port}`);  
  36. });  

The first three lines do the basic configuration of setting up the express module and make the application runnable and listen to requests at a specific port number. (It looks if the port number is already configured in the form of an environment variable, if not it explicitly sets the port number to 3000).

Next comes the job of adding middlewares to the express request handling pipeline. Note that all the middlewares are wrapped inside the app.use() method.

  1. app.use(express.static(__dirname + "/public"));  
  2. --express.static is an in -built middleware provided by express which is used to serve static files from a particular path(passed as an argument) when requested by the user.  
  3. app.use((req, res, next) => {  
  4.     if (req.url.indexOf("books") > -1) {  
  5.         res.status(401).send();  
  6.         --exits the pipeline and returns the response back to the browser  
  7.     } else {  
  8.         console.log(req.url);  
  9.         next();  
  10.         -- //calls the next middleware/request handler in the pipeline  
  11.     }  
  12. });  

Next comes the custom middleware which kind of simulates a logic to perform some authentication on a protected route before actually moving the execution to the request handler. In our example, we take the assumption that we are making the request to the route "/books" as an anonymous user without any authentication and thereby, any attempt to access the mentioned route should be returned with a status code of 401 (Unauthorized). Otherwise, if the user makes a request to some other route, the middleware will do its job and the request will then flow down the pipeline to execute the next middleware or request handler. In this case, it shall move to the next middleware.

  1. app.use((req, res, next) => {  
  2.     console.log(`${req.url} route hit at ${new Date().toLocaleDateString()}`);  
  3.     next();  
  4.     -- //calls the next middleware/request handler in the pipeline  
  5. });  

This middleware shall simply log the time of the request to the console which in a way can be seen as an analogue to performing some kind of logging before the request is processed by the application.

Note that if we omit the next() call here and the user makes a request to the "/" route, the request will not be able to move to the request handler and the application will get stuck.

  1. app.get("/", (req, res, next) => { //request handler for the "/" route  
  2.     res.send("root request route..."); //sends the response back to the browser  
  3.     next();  
  4.     --calls the final middleware in the pipeline  
  5. });  

After traversing down a couple of middlewares, then if the request url matches to the root route i.e. "/", the request handler provided above shall be triggered and shall return the desired response back to the browser. Post sending the response, the next() function will again invoke the next middleware if any is in the request pipeline.

  1. app.use("/", (req, res) => {  
  2.     console.log("From the Terminal middleware...");  
  3.     console.log("Doing the post request handling cleanup...");  
  4. });  

This middleware function does the cleanup work post request handling which in this case is simply logging something to the console. Note that this middleware gets invoked specifically when the "/" route is accessed by the user as denoted by the first argument to the app.use() method. This implies that we can also cause a middleware to be included in the pipeline only when the request is made to a specific route.

One more thing that needs to be taken into account is the order of execution of the middleware functions. The functions get executed in the same order in which they are declared.

As a brief side note, Express also provides us with an error handling middleware that automatically takes care of catching the exception encountered in any of the middlewares in the pipeline and then aggregates them and sends back as a response.

That's all I have for Express middlewares. Please feel free to drop in your views on the same as I look forward to being back with some more interesting content very soon.

Happy Coding!!!