“We live in a world that has walls and those walls need to be guarded by men with guns.”
― Aaron Sorkin, A Few Good Men
Introduction
Time sure flies. It’s already the fourth part of the “How to Create REST API Using Node.js”. If you are following these series until now and reading this post, thank you very much! It pleases me that I can do something useful for others.
Should you stumble upon this post first, you can find the previous posts here:
Alright then, now to the real intro for this post. On the last previously mentioned three posts, we learned about using Node.js to create a REST API application with Express. On the second part, We also learned about data operations using MongoDB and integrate it to the application. Then, the third part was about implementing cache with Redis to improve the performance of our REST API application.
But, our application is not secure because anyone can access it. We need to add some security to our system. Let’s start by securing our API routes by adding JWT. As usual, I will share the source code on GitHub at the end of this post.
Requirements
JWT Library
It’s simple this time, we just need to install jsonwebtoken to the project. On the terminal, execute npm i jsonwebtoken
.
If you want to start this part fresh, please clone from this repository.
Part 4 – Improve API Routes Security by Implementing JWT
POST /login
First, we revisit our POST /login
route and modify it to its real purpose after being neglected for 3 parts of tutorial.
index.js
Let’s modify POST /login
from index.js
. See this snippet below:
// index.js
...
app.post('/login', async (req, res) => {
const { username, password } = req.body;
if (username && password) {
const result = await mongoDbConnector.findOne(
collection,
{ username, password }
);
if (result !== 'Data not found.') {
const update = await mongoDbConnector.updateOne(
collection,
{ username, password },
{ lastLogin: new Date(), logged: true }
);
res.send({
status: 'success',
message: `Logged as ${username}`
});
}
else {
res.send({
status: 'error',
message: 'Wrong username or password!'
});
}
}
else {
res.send({
status: 'error',
message: 'Username or password field is missing!'
});
}
});
...
We will modify app.post('/login')
. The callback function is set to be asynchronous, because there will be some DB operations.
First, we fetch the username
and password
from req.body
. If any of these fields are missing, the response from POST /login
will be an error “Username or password field is missing!”.
Then, if username
and password
exists, it tries to find a document containing those credentials with mongoDbConnector.findOne()
. If it is not found, it will return “Data not found.” which will result in POST /login
route will return an error response “Wrong username and password!” from res.send()
.
If mongoDbConnector.findOne()
has a result, it will pass through the if
logic and call mongoDbConnector.updateOne()
to update the lastLogin
time. Lastly, since all the functions were executed successfully, the POST /login
route will return a success response from res.send()
.
Test The code
Run npm start
from your Terminal. Then put the credentials username
and password
as a JSON object. Finally, hit POST /login
via Postman, and you will see these kind of screenshots should you code it correctly:



Notes
I am aware it’s not secure yet because the password is not encrypted, and the user is not exactly logged in because there is no sessions here. We will get to that later on. Let’s just take one step at a time.
Add JWT
We will now create a JSON web token generator and checker functionality using the jsonwebtoken
library.
authUtils.js
Create a new file named authUtils.js
which contains the JWT module. See this snippet below:
// authUtils.js
const jwt = require('jsonwebtoken');
const createToken = (key, data, expiry) => {
return jwt.sign(data, key, {
expiresIn: expiry
});
};
module.exports = {
createToken
};
First, we import the jsonwebtoken
module. Then, we create a function called createToken()
. It will return a token generated from jwt.sign()
with the arguments of data
, key
, and an object which currently only has the property of expiresIn
.
.env and .env.example
On .env
, add some new environment variables for generating new tokens. See this snippet below:
...
AUTH_KEY=cil-nodejs-api
AUTH_EXPIRY=600000
...
AUTH_KEY
is the key
for creating a new token, and AUTH_EXPIRY
is the time needed for the token to expire in milliseconds. That means 600000
is 10 minutes.
Index.js
Open index.js
to import authUtils
. We will use the newly created module to create a new token when login is successful. See the snippet below:
// index.js
const config = {
app: {
...,
auth: {
key: env.AUTH_KEY,
expiry: env.AUTH_EXPIRY
}
},
...
}
...
const { ObjectId } = require('mongodb');
const authUtils = require('./authUtils');
...
app.post("/login", async (req, res) => {
...
if (username && password) {
...
if (result !== 'Data not found.') {
const { auth } = config.app;
...
const token = authUtils.createToken(auth.key, { username, password }, auth.expiry);
res.send({
status: 'success',
message: `Logged as ${username}`,
token
});
}
}
...
});
...
First, we import the new environment variables as config.app.auth.key
and config.app.auth.expiry
.
Second, we import the previously created authUtils
with const authUtils = require('./authUtils');
Third, on app.post("/login")
, if the login credentials match, we create a new token with a key
from config.app.auth.key
, data
from { username, password }
, and expiry
from config.app.auth.expiry
.
Finally, if the token is created successfully, it will be returned as a response alongside status
and message
.
Test the code
Run npm start
on your Terminal, then start the Postman app to hit POST /login
. If everything goes well, you will see this kind of result:

Verify Token via Middleware
Next, we will add a middleware to all the routes for checking whether the token is valid on request.
authUtils.js
First, we add verifyToken()
to check if the token is valid. See this snippet below:
// authUtils.js
const jwt = require('jsonwebtoken');
const verifyToken = (token, key) => {
return jwt.verify(token, key);
};
...
Yeah, it’s a simple code. We create a function named verifyToken()
and just return the result from jwt.verify()
. The result is the data you create from createToken()
.
authMiddleware.js
Next, we create a middleware module called authMiddleware.js
. We will add this same middleware to every routes except POST /login
and POST /logout
.
const { verifyToken } = require('./authUtils');
const authMiddleware = (req, res, next) => {
const { auth } = req.headers;
const { config } = req.app;
try {
verifyToken(auth, config.app.auth.key);
next();
}
catch (err) {
res.status(401).send(err);
}
};
module.exports = authMiddleware;
We import verifyToken()
from authUtils
. Then we create a function called authMiddleware()
. This function needs three parameters: req
, res
, next
. Every middleware functions requires these three parameters, at least in Express.
Inside the function, we extract auth
from req.headers
. This means we will put auth
as one of the headers on requesting on the route which passes this middleware.
Then, we get config
from req.app
. Yes, this is the configuration from loading environment variables. We will cover on how to set this later on index.js
.
Finally, make a try-catch function. In the try block, call verifyToken()
to verify the token’s validity. If it’s successful, it passes the authentication. But, if it fails, the catch block will return a response that the token verification failed.
index.js
Last part, we are back to index.js
, where we will create a new config middleware, then apply the middlewares on the routes we created. Just for example only, I will only cover one route: GET /user
, because the rest of the routes (POST, PATCH, DELETE) can also be applied in the same easy way. See this snippet below:
// index.js
...
const authUtils = require('./authUtils');
const authMiddleware = require('./authMiddleware');
...
const configMiddleware = async (req, res, next) => {
Object.assign(req.app, { config });
next();
};
...
app.get('/user', configMiddleware, authMiddleware,
async (req, res) => {
const result = await mongoDbConnector.find(collection, {});
res.send(result);
}
);
...
First, we import authMiddleware
we created before.
Remember that we created config
object to save environment variables? Now we want to make that config
able to be accessed in any route we want. So, we create a new middleware to assign config
to req.app
. The routes that access this middleware will be to able to get req.app.config
.
Lastly, we modify app.get("/user")
to execute configMiddleware
first, then authMiddleware
, then the handler to load the data from MongoDB and return the response.
Test the code
Test the code with npm start
. Open your Postman and hit GET /user
. If everything went smoothly for you, you should get some results like these:



So, to pass this error, you have to login first by hitting POST /login
. Then, get the token from here:

Put the token to the auth
header of GET /user
, and it should return a success response.

Conclusion
Congrats! You have reached the ending. A quick recap of what we have learned:
- Installing
jsonwebtoken
library. - Implement
createToken()
on successful login - Implement
verifyToken()
on any route. - Learn some general errors in token verification.
It would seem rather complicated to add an authentication token, but it’s not, actually. Because by using a middleware, you can authenticate every route you want.
The JWT in this tutorial is the simplest form of authentication. You can read more about other encryption provided by jsonwebtoken, and find which kind you need for your system.
Finally, here is the source code for this post. Let me know what you missed, or what I missed in the comments!
As you may have noticed, there are some issues in the code such as the user’s password are not encrypted in the database or some structure inconsistencies such as configMiddleware
is index.js
, while authMiddleware
is separated on another file.
So, as you may also have guessed, the next part is about refactoring for more structured and readable code. Stay tuned!
Next post in the series:
Part 5 – Code Refactor Because It’s Starting To Get Complicated
“Human security depends on a system where each rational individual calculates that it is more profitable not to rebel.”
― Mark Gough