Introduction to Express.js - Middleware

Middleware functions are important ingredients to any Node and Express apps. In the last part of the series on Express.js I will discuss what middleware is and how we can use it to make our app better.

Part 1: Introduction to Express.js: Setting up Express.js and basic routing

Part 2: Status codes and requests

After a short introduction to Express.js and http status codes, I’ll briefly talk about the concept of middleware.

What is middleware?

Middleware is nothing else but a piece of function. If we have a thorough look at an Express app, it’s basically built from middleware.

They have access to the request and the response objects and can modify them by adding extra properties.

How to use them

Middleware as the name suggests is used somewhere “in the middle” of the flow and performs some additional action. Let’s see some basic examples below.

app.use()

One of the most frequent ways of applying middleware to our code is the use method on the app object. app.use accepts a callback function, which should have at least three arguments: req (referring to the request object), res (conventional notation for response) and next, which is a function. next should be called if we want to pass the control to the next function in our code (and we want to in most cases), else everything else coming after line of the middleware function won’t be read.

If we go back to the example from the last post, we can insert a middleware function in the code like this:

// a middleware function
app.use((req, res, next) => {
  console.log('I \'m inside my middleware!');
  next();
});

app.get('/', (req, res) => {
  res.status(200).send('Response from server');
});

If we start the server with node server.js (or restart it if it’s running), we will see the message “I’m inside my middleware!” in the console and the “Response from the server” message in the browser.

Now remove the next() call from the callback and see what happens:

app.use((req, res, next) => {
  console.log('I \'m inside my middleware!');
});

After restarting the server and refreshing the browser we’ll see the message in the console but won’t see the “Response from the server” message in the browser. Instead, the browser just hangs and waits for the connection to establish.

This will, unfortunately, never happen. As the code is read from top to bottom, not calling next() inside the middleware causes the read flow to stop. We will never make it to the bottom of the file where the app.listen(..) call is located and connects our server to the browser.

Authentication

Middleware functions are frequently used for actions that are always taken before a particular endpoint is hit. One such action is authentication.

If we have a path which is only accessible to certain users, we can write an authentication middleware function and insert it inside our route handlers.

Let’s see how this works in a simple example. Assume that the /me endpoint can only be accessed by certain users. I’m me, after all, and not anyone else.

Create a new file called middleware.js next to server.js in the root folder and we can write something like this in the file:

module.exports.authenticate = (req, res, next) => {
  // authentication is usually done async but a console.log will do for now:
  console.log('Authenticated!');
  next();
};

Although not necessary, it’s a good idea to extract this function to a separate file. As such, we have to export it from the file and import it at the top of server.js:

const { authenticate } = require('./middleware');

We can use object destructuring to get the function. We save the authenticate function as a property of the exports object. With destructuring we can extract this function from exports and save it to the variable of the same name, authenticate. The {} around authenticate indicates that we are getting the property from an object.

Let’s place autheticate inside the /me GET route handler:

app.get('/me', authenticate, (req, res) => {
  res.status(200).send(`My name is ${ name } and I'm saying hi from the server`);
});

Organizing the code this way makes it more readable compared to writing the whole middleware function inside the route handler.

Now restart the server and navigate to localhost:3000/me and the friendly message should appear in the browser as well as the “I’m inside my middleware!” and “Authenticated” messages in the console. Cool!

So what’s happening here? The middleware is again in the middle: we call the /me route with the get method first. Then we call authenticate as a middle step before the /me path is handled by the callback of the get method. Once the authenticate middleware does its job (i.e. prints the message to the console), and strictly after that occurs, it passes the control to the route handler callback, and the server sends the response to the client so that we can read the friendly message.

Note that this is an extremely simplified way of using middleware. Authentication is a lot more complicated and it’s usually done asynchronously. A more realistic authentication middleware function looks like this:

module.exports.authenticate = async (req, res, next) => {
  try {
    // some authentication code with await as the database is contacted for credentials
    next();
  } catch (error) {
    res.status(401).send('Authentication failed.');
  }
};

Now let’s move on to some other types of middleware.

Built-in middleware

Express comes with some pieces of built-in middleware. Perhaps the most often used of all is express.static.

This method is used to display static contents, like HTML, CSS and files. Let’s see how this can be done through a simple example.

Create a folder called public and an index.html file inside this folder. We can write the following basic mark-up in index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Introduction to Express.js</title>
</head>
<body>
  <h1>Express.js</h1>
  <p>A paragraph from the static HTML file.</p>
</body>
</html>

index.html is a static file and we can access it using express.static:

app.use('/', express.static(path.join(__dirname, '/public')));

We need to first import the path module of Node.js at the top of the file:

const path = require('path');

Now let’s go over this line step-by-step.

We use the already familiar app.use method here, which can have an optional first parameter, the path, where we want to access our static files. In this case, this will be localhost:3000, so we need to write /.

The second argument is the express.static middleware, which accepts another path as a parameter. But this path refers to the folder in the local file system where the static files are located. As we placed our static index.html file in the public folder, we will write /public in here.

The twist here is the join method on the path module. join concatenates its arguments and creates a path. __dirname always refers to the folder where the file from which the script is run is located. In our case, __dirname will be the root folder of the project since we wrote the code in server.js. If we, say, had placed server.js in a folder called server, that would be __dirname.

This is how path.join(__dirname, '/public') will give us the correct path to the static file location relative to server.js.

Let’s now create an html folder inside public and move index.html into this new folder. Say we want to access the index.html file when the localhost:3000/static/index.html endpoint is hit. We then need to change the middleware accordingly:

app.use('/static', express.static(path.join(__dirname, '/public/html')));

Now we can see the content of the HTML file at localhost:3000/static/index.html after the server has restarted. Easy.

Standalone middleware modules

We can make use of some good middleware modules in our Express applications. They can also be applied inside the app.use method and they are best used at the top of the file before the route handlers are declared.

The Helmet module sets some HTTP headers to make the app more secure. It’s worth reading the documentation.

The body-parser parses the body of the request object, which is a stream, into a more digestable format, and passes the parsed body onto req.body. A frequently used method of body-parser is the json(), which makes the incoming content readable in JSON.

If the reader is interested, they can read more on the middleware modules on the Express website.

Conclusion

We have played around with some Express.js features. Of course, reality is much more complex and more difficult. A real-life application can have dozens of routes, makes extensive use of both req and res (i.e. the request and response objects), can have various middleware functions, serve lots of static files, can make use of view engines etc. Only the foundations i.e. the building blocks were covered in the last three posts.

With the increasing popularity of serverless, does Express have a room in the developer’s toolkit? I think the answer is yes. It’s a great basic tool and it doesn’t take long to create a simple web server for my playground and real apps.

It’s true though that scaling is not as simple with Express as is with serverless. One has to be familiar with lots of other packages and tools (like Docker) if they want to keep their app scalable.

Express is a great framework to create REST APIs but competitors have come up and are increasing their share in the developer’s market.

I still think that one must know Express and how to use it at least at a basic level. These posts are meant to help in this.

Thanks for reading.