Middleware in Express.js
Let’s say there is a route that you want only authenticated users to access. In this case, you will put up a check that will happen every time a request to this protected route is made. If the request is made by an authenticated user, then you will let the process proceed. But, if it is made by an unauthenticated user, then you will send an error message. These checks are performed by functions called middlewares.
The above is not the definition of middleware, but is a sample use case for them, and one of the most frequent use cases of middleware in Express.js.
In formal terms, a middleware function has access to the request object, response object, and access to the next function. This next function is called when we want the next middleware or function in the sequence to be called.
We can use middleware to do the following tasks:
-
Execute code on our server
-
Modify request/response objects
-
End the current request/response cycle
-
Call next middleware
Throughout this article, we will learn how to do these tasks.
Let’s start with some hands-on work. Create an empty folder and initialize a node project by running npm init and then install express by running npm i express.
Set up an express server by creating a file called app.js which has the following code inside of it.
1
2
3
4
5
6
const
express =require
('express'
);const
app = express(); app.listen(3000
, () => {console
.log('Server started'
); });
On running the command nodemon app.js, the following should appear in your terminal.
Let’s start by creating a middleware that gets executed for every request. Add the following code right after you initialize the app variable.
1
2
3
4
5
6
7
8
9
10
const
middlewareFunction =function
(req, res, next
) {console
.log('We are in middleware'
); next(); }; app.use(middlewareFunction); app.get('/'
, (req, res) => { res.send('inside the / route'
); });
First, we have created a function called middlewareFunction which takes the req, res, next as arguments. Whenever this middleware will get called it will print We are in middleware to the console and call the next function. Since we are using it here for each route with the help of this app.use(middlewareFunction);
code snippet, it will be called for each request. Let’s now send a request to the http://localhost:3000 using postman and see the console. Here, we are executing code on our server which is one of the tasks of middleware mentioned above.
As you can see, our middleware was executed before our request.
There are several uses for this middleware. For example, you can store request logs in your database.
Now, we will make our middleware work for only specific requests. Remove the app.use() line and modify the API call like this:
1
2
3
4
app.get(
'/'
, middlewareFunction, (req, res) => {console
.log('inside the / route'
); res.send('Route hit'
); });
Let’s create another API without middleware.
1
2
3
4
app.get(
'/no-middleware'
, (req, res) => {console
.log('inside the /no-middleware route'
); res.send('Route hit'
); });
Now let’s make a request to both the routes.
-
Request to / route (middleware gets called)
-
Request to /no-middleware (middleware does not get called)
Let’s try and create a protected route to filter out unauthenticated users using middleware.
Note: In the following example we will randomly select a value and according to that value we will judge whether the user is authorized or not. This is not how it is done in real applications, it is just to show you how middleware is used in the login process. If you want to see how it works in real applications, see this article which primarily focuses on authentication – Authentication and Authorization in Express.
Modify your middleware function like this.
1
2
3
4
5
6
7
const
middlewareFunction =function
(req, res, next
) {const
random =(
min, max
) =>Math
.floor(Math
.random() * (max - min)) + min;const
isAuthenticated = random(0
,2
);if
(isAuthenticated) next();else
res.send('User is not authorized'
); };
Here we are assigning a random value to the isAuthenticated variable. If it is one then we are moving on with our process. Otherwise, we terminate this cycle by sending the message to the user.
In the end our app.js file looks like this.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const
express =require
('express'
)const
app = express();const
middlewareFunction =function
(req, res, next
) {const
random =(
min, max
) =>Math
.floor(Math
.random() * (max - min)) + min;const
isAuthenticated = random(0
,2
);if
(isAuthenticated) next();else
res.send('User is not authorized'
) }; app.get('/'
, middlewareFunction, (req, res) => {console
.log('inside the / route'
); res.send('Route hit'
); }); app.get('/no-middleware'
, (req, res) => {console
.log('inside the /no-middleware route'
); res.send('Route hit'
); }); app.listen(3000
, () => {console
.log('Server started'
); });
In the above piece of code, we are ending the request/response cycle for unauthenticated users and also modifying the response object by sending a message of the user being unauthenticated. We are calling the next() middleware for the authenticated users.
Working with asynchronous middlewares:
Working with asynchronous middlewares is similar to working with non-asynchronous middlewares. All we have to do is add the async-await keywords in front of our middleware functions.
Modify the middleware like this.
1
2
3
4
5
6
7
8
9
const
middlewareFunction =async
function
(req, res, next
) {const
random =(
min, max
) =>Math
.floor(Math
.random() * (max - min)) + min;let
isAuthenticated;await
setTimeout(()
=> { isAuthenticated = random(0
,2
);if
(isAuthenticated) next();else
res.send("User is not authorized"
); },3000
); };
Here I have added an international delay of three seconds, which makes the function asynchronous.
We will still add it in our route the same way.
1
2
3
4
app.get(
'/'
, middlewareFunction, (req, res) => {console
.log('inside the / route'
); res.send('Route hit'
); });
Now let’s make a request.
You will see that it waits for some time before giving back the output, which means the code is working fine.
Adding third party middlewares:
If you want to add any third-party middleware to your code then you will have to work according to the documentation provided on the middleware’s website. For example, let’s take a look at Helmet.
It’s a middleware that protects your app by setting up various headers.
For example:
-
Open localhost:3000 on browser
-
Open network tab in console.
-
If it’s empty, then reload with the network tab open, you should see something like this.
-
Click on this request. You will see this.
-
It is announcing to the world that you are using an express app at the backend, because of the X-Powered-By: Express object. People with malicious intent can do attacks that are known to work specifically against Express-based apps.
We can use helmet to hide these vulnerabilities.
Using helmet:
-
Open terminal and run npm i helmet.
-
Import helmet in your main file. Using
const helmet = require("helmet");
-
Since we want the helmet to work on every request, we will add this line before any request.
app.use(helmet());
Now, our headers will have no info indicating that we are using express.
Click to show preference!
Click to show preference!