Building and securing restful apis
Now that you know what you will create and what the prerequisites are, it’s time to starting building your application. For starters, open a terminal, move it to the directory where you usually create your projects, and create a new directory there:
mkdir express-ads-api
Then, move into this new directory and use npm to scaffold a new project:
npm init -y
The command above will scaffold the project with some default properties. If you open this directory in a text editor or in an IDE (like Visual Studio Code or WebStorm), you will see that the npm command you issued created a file called package.json. Opening this file, you will see the following contents:
Configure auth key
jsonwebtoken functions such as verify() or sign() use algorithm that needs a secret key (as String) to encode and decode token.
In the app/config folder, create auth.config.js file with following code:
module.exports = {
secret: "bezkoder-secret-key"
};
You can create your own secret String.
Configure node.js and passport.js application
We will make separate files for the configuration. For that, create the new folder on the root folder.
mkdir config
Create a configuration file for Database and Passport.js.
touch config/database.js
touch config/passport.js
Open and edit “config/database.js” then add these lines of codes.
module.exports = {
'secret':'nodeauthsecret',
'database': 'mongodb://localhost/node-auth'
};
This config holds the database connection parameter and secret for generating a JWT token.
Open and edit “config/passport.js” then add these lines of codes.
Create node.js app
First, we create a folder for our project:
$ mkdir node-js-jwt-auth
$ cd node-js-jwt-auth
Then we initialize the Node.js App with a package.json file:
npm init
name: (node-js-jwt-auth)
version: (1.0.0)
description: Node.js Demo for JWT Authentication
entry point: (index.js) server.js
test command:
git repository:
keywords: node.js, express, jwt, authentication, mysql
author: bezkoder
license: (ISC)
Is this ok? (yes) yes
Creating your first express api
Right now, the project you created just logs a static message. As this is not very useful, after building your “Hello, world!” application with Node.js, you can start focusing on creating a RESTful API. For that, the first thing you will need is to install some dependencies. So, head to your terminal and issue the following command:
npminstall body-parser cors express helmet morgan
This command will install five dependencies in your project:
Note: After issuing the command above, you will notice two things in your project. First, the package.json
file will contain a new property called dependencies
with all the libraries above. This is how NPM knows what dependencies your project needs. Second, you will notice a new file called package-lock.json
inside the project root. This file helps NPM identify what are the exact libraries you used while developing, so it uses the same ones everywhere (i.e., in other environments).
When NPM finishes installing these dependencies (it might take a few seconds, depending on your internet connection), you can open the index.js file, and replace its code with the following:
const express =require('express');const bodyParser =require('body-parser');const cors =require('cors');const helmet =require('helmet');const morgan =require('morgan');const app =express();const ads =[{title:'Hello, world (again)!'}];
app.use(helmet());
app.use(bodyParser.json());
app.use(cors());
app.use(morgan('combined'));
app.get('/',(req, res)=>{
res.send(ads);});
app.listen(3001,()=>{
console.log('listening on port 3001');});
The new version of this file starts by importing all the dependencies you installed moments ago, goes through the creation and configuration of a new Express application (const app =express()), and ends by making this application listen on port 3001 (app.listen(3001,…)). Besides that, this code defines two important things:
Note: The code snippet above contains comments that can help you understand each line. If you want to learn more about the middleware being used (i.e., about helmet
, bodyParser
, cors
, and morgan
), please, refer to their official documentation.
Further reading
Fullstack CRUD Application:- Vue.js Node.js Express MySQL example- Vue.js Node.js Express MongoDB example- Angular 8 Node.js Express MySQL example- Angular 10 Node.js Express MySQL example- Angular 11 Node.
Deployment:- Deploying/Hosting Node.js app on Heroku with MySQL database- Dockerize Node.js Express and MySQL example – Docker Compose
Integrating express and mongodb
When it comes to databases, the most popular choice among Node.js developers is (by far) MongoDB. This database engine allows developers to use a flexible document data model that plays particularly well with Node.js apps. As you will see throughout the article, manipulating a MongoDB database from a Node.js application is easy and efficient.
Before learning about how to make your Express API operate MongoDB though, you will need a database instance. For that, you have several options like installing MongoDB in your machine, running it in a container, or using a cloud provider like MongoDB Atlas.
However, to facilitate the process, you will use a package called mongodb-memory-server that spins up a MongoDB instance programmatically for testing or mocking during development.
So, back into your terminal, use npm to install these packages:
npm i mongodb-memory-server mongodb
After installing them, create a new directory called database inside the src directory and, inside it, create a new file called mongo.js. Inside this file, add the following code:
const{MongoMemoryServer}=require('mongodb-memory-server');const{MongoClient}=require('mongodb');let database =null;asyncfunctionstartDatabase(){const mongo =newMongoMemoryServer();const mongoDBURL =await mongo.getConnectionString();const connection =await MongoClient.connect(mongoDBURL,{useNewUrlParser:true});
database = connection.db();}asyncfunctiongetDatabase(){if(!database)awaitstartDatabase();return database;}
module.exports ={
getDatabase,
startDatabase,};
As you can see, this file exports two functions. One to initialize the in-memory database (startDatabase) and one that returns a reference to it (getDatabase).
With that in place, create a new file called ads.js inside the database directory and add the following code to it:
const{getDatabase}=require('./mongo');const collectionName ='ads';asyncfunctioninsertAd(ad){const database =awaitgetDatabase();const{insertedId}=await database.collection(collectionName).insertOne(ad);return insertedId;}asyncfunctiongetAds(){const database =awaitgetDatabase();returnawait database.collection(collectionName).find({}).toArray();}
module.exports ={
insertAd,
getAds,};
Login app – create rest api for authentication in node.js using jwt – part 2 – clue mediator
require(‘dotenv’).config();
constexpress=require(‘express’);
constcors=require(‘cors’);
constbodyParser=require(‘body-parser’);
constjwt=require(‘jsonwebtoken’);
constutils=require(‘./utils’);
constapp=express();
constport=process.env.PORT||4000;
// static user details
constuserData={
userId:“789789”,
password:“123456”,
name:“Clue Mediator”,
username:“cluemediator”,
isAdmin:true
};
// enable CORS
app.use(cors());
// parse application/json
app.use(bodyParser.json());
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended:true}));
//middleware that checks if JWT token exists and verifies it if it does exist.
//In all future routes, this helps to know if the request is authenticated or not.
app.use(function(req,res,next){
// check header or url parameters or post parameters for token
vartoken=req.headers[‘authorization’];
if(!token)returnnext();//if no token, continue
token=token.replace(‘Bearer ‘,”);
jwt.verify(token,process.env.JWT_SECRET,function(err,user){
if(err){
returnres.status(401).json({
error:true,
message:“Invalid user.”
});
}else{
req.user=user;//set the user to req so other routes can use it
next();
}
});
});
// request handlers
app.get(‘/’,(req,res)=>{
if(!req.user)returnres.status(401).json({success:false,message:‘Invalid user to access it.’});
res.send(‘Welcome to the Node.js Tutorial! – ‘ req.user.name);
});
// validate the user credentials
app.post(‘/users/signin’,function(req,res){
constuser=req.body.username;
constpwd=req.body.password;
// return 400 status if username/password is not exist
if(!user||!pwd){
returnres.status(400).json({
error:true,
message:“Username or Password required.”
});
}
// return 401 status if the credential is not match.
if(user!==userData.username||pwd!==userData.password){
returnres.status(401).json({
error:true,
message:“Username or Password is Wrong.”
});
}
// generate token
consttoken=utils.generateToken(userData);
// get basic user details
constuserObj=utils.getCleanUser(userData);
// return the token along with user details
returnres.json({user:userObj,token});
});
// verify the token and return it if it’s valid
app.get(‘/verifyToken’,function(req,res){
// check header or url parameters or post parameters for token
vartoken=req.body.token||req.query.token;
if(!token){
returnres.status(400).json({
error:true,
message:“Token is required.”
});
}
// check token that was passed by decoding token using secret
jwt.verify(token,process.env.JWT_SECRET,function(err,user){
if(err)returnres.status(401).json({
error:true,
message:“Invalid token.”
});
// return 401 status if the userId does not match.
if(user.userId!==userData.userId){
returnres.status(401).json({
error:true,
message:“Invalid user.”
});
}
// get basic user details
varuserObj=utils.getCleanUser(userData);
returnres.json({user:userObj,token});
});
});
app.listen(port,()=>{
console.log(‘Server started on: ‘ port);
});
Node.js express architecture with authentication & authorization
You can have an overview of our Node.js Express App with the diagram below:
Prerequisites
To follow along with this article, you will need to have prior knowledge around JavaScript. If you have never used JavaScript before (even for frontend development), you might not understand the article well and it might make sense to learn about it first.
If you do have previous experience with JavaScript, but you haven’t used Node.js, don’t worry, you won’t have a hard time here. Although it would be ideal to know a bit about Node.js, you will see that the code and the concepts explained in this article are not complex.
Other than that, you will need to have Node.js and NPM installed in your machine. If you don’t have these, please, follow the instructions over here.
Project structure
This is directory structure for our Node.js Express application:
– config
- configure MySQL database & Sequelize
- configure Auth Key
– routes
– middlewares
– controllers
– models for Sequelize Models
– server.js: import and initialize necessary modules and routes, listen for connections.
Running and testing with insomnia
Insomnia is a decent REST client with a good free version. The best practice is, of course, to include code tests and implement proper error reporting in the project, but third-party REST clients are great for testing and implementing third-party solutions when error reporting and debugging the service is not available. We’ll be using it here to play the role of an application and get some insight into what is going on with our API.
Securing express apis with auth0
Right now, you have an Express API that exposes endpoints that allow clients to insert, update, delete, and retrieve ads. This is a nice start, but you could use some security, right?
Setup express web server
In the root folder, let’s create a new server.js file:
Source code
You can find the complete source code for this tutorial on Github.
If you want to use Cookies, please visit:Node.js Express: Login and Registration example with JWT
Step 1 – create database and table
Execute the following command on terminal to create database and table:
Step 2 – create node express js app
Execute the following command on terminal to create node js app:
mkdir nodejs-auth-rest-api-mysql cd nodejs-auth-rest-api-mysql npm init -y
Step 4 – install express and required modules
Execute the following command on terminal to install express express-validator mysql body-parser jsonwebtoken bcryptjs cors into your node js express app:
npm install express express-validator mysql body-parser jsonwebtoken bcryptjs cors --save
Step 5 – create server.js file
Create server.js file and import express express-validator mysql body-parser jsonwebtoken bcryptjs cors into your server.js file; as shown below:
Step 6 – create validation.js, router.js and dbconnection.js
Create validation.js and router.js. So visit your app root directory and create this files.
Then add the following code into your validation.js file:
Then add the following code into your router.js file:
const express = require('express');
const router = express.Router();
const db = require('./dbConnection');
const { signupValidation, loginValidation } = require('./validation');
const { validationResult } = require('express-validator');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
router.post('/register', signupValidation, (req, res, next) => {
db.query(
`SELECT * FROM users WHERE LOWER(email) = LOWER(${db.escape(
req.body.email
)});`,
(err, result) => {
if (result.length) {
return res.status(409).send({
msg: 'This user is already in use!'
});
} else {
// username is available
bcrypt.hash(req.body.password, 10, (err, hash) => {
if (err) {
return res.status(500).send({
msg: err
});
} else {
// has hashed pw => add to database
db.query(
`INSERT INTO users (name, email, password) VALUES ('${req.body.name}', ${db.escape(
req.body.email
)}, ${db.escape(hash)})`,
(err, result) => {
if (err) {
throw err;
return res.status(400).send({
msg: err
});
}
return res.status(201).send({
msg: 'The user has been registerd with us!'
});
}
);
}
});
}
}
);
});
router.post('/login', loginValidation, (req, res, next) => {
db.query(
`SELECT * FROM users WHERE email = ${db.escape(req.body.email)};`,
(err, result) => {
// user does not exists
if (err) {
throw err;
return res.status(400).send({
msg: err
});
}
if (!result.length) {
return res.status(401).send({
msg: 'Email or password is incorrect!'
});
}
// check password
bcrypt.compare(
req.body.password,
result[0]['password'],
(bErr, bResult) => {
// wrong password
if (bErr) {
throw bErr;
return res.status(401).send({
msg: 'Email or password is incorrect!'
});
}
if (bResult) {
const token = jwt.sign({id:result[0].id},'the-super-strong-secrect',{ expiresIn: '1h' });
db.query(
`UPDATE users SET last_login = now() WHERE id = '${result[0].id}'`
);
return res.status(200).send({
msg: 'Logged in!',
token,
user: result[0]
});
}
return res.status(401).send({
msg: 'Username or password is incorrect!'
});
}
);
}
);
});
router.post('/get-user', signupValidation, (req, res, next) => {
if(
!req.headers.authorization ||
!req.headers.authorization.startsWith('Bearer') ||
!req.headers.authorization.split(' ')[1]
){
return res.status(422).json({
message: "Please provide the token",
});
}
const theToken = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(theToken, 'the-super-strong-secrect');
db.query('SELECT * FROM users where id=?', decoded.id, function (error, results, fields) {
if (error) throw error;
return res.send({ error: false, data: results[0], message: 'Fetch Successfully.' });
});
});
module.exports = router;
The router.js file has been contained three routes; as shown below:
Steps to build a secure node js rest api
Building a Node js REST API is a four-step process. Follow the steps given below to build a secure Node js REST API:
Table of contents:
Until now, Passport.js still a robust, flexible, and modular authentication middleware for Node.js environment. So, in this tutorial, you will see a lot of Passport.js domination. Authentication mechanism to generate JSON web token (JWT), all handled by Passport.js. The Express job just handle the API routing, the middleware for accessing the MongoDB database is handled by Mongoose.js.
What is rest api?
A RESTful API, also known as REST API, is based on Representational State Transfer (REST), which is an architectural style and communication approach commonly used in the development of Web Services.
In general, REST technology is preferred over other similar technologies. This is because REST consumes Less Bandwidth, making it more suitable for efficient Internet usage. RESTful APIs can also be designed using programming languages like JavaScript or Python.
Run & test with results
Run Node.js application with command: node server.js
Tables that we define in models package will be automatically generated in MySQL Database.If you check the database, you can see things like this:
Initialize sequelize
Now create app/models/index.js with content like this: