DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Building REST API Backend Easily With Ballerina Language
  • Composite Requests in Salesforce Are a Great Idea
  • Secure API Design With OpenAPI Specification
  • REST APIs Test Pyramid

Trending

  • Role of Cloud Architecture in Conversational AI
  • Mastering Advanced Aggregations in Spark SQL
  • MySQL to PostgreSQL Database Migration: A Practical Case Study
  • DGS GraphQL and Spring Boot
  1. DZone
  2. Coding
  3. JavaScript
  4. Creating a Secure REST API in Node.js

Creating a Secure REST API in Node.js

Step by step guide to create a REST API using Node.js and create your first app in Express API.

By 
Michael Smit user avatar
Michael Smit
·
Updated Aug. 06, 21 · Tutorial
Likes (12)
Comment
Save
Tweet
Share
20.2K Views

Join the DZone community and get the full member experience.

Join For Free

Table of Content

  1. Introduction
  2. The Importance of Node.js for Rest API!
  3. What is REST and how does it blend with Node.js?
    1. Creating and Securing RESTful APIs in Node.js!
    2. Creating your first app Express API
    3. Creating the User Module
    4. Creating the Auth Module
  4. Conclusion

1. Introduction

The hype of Application programming interfaces (APIs) is universal. They enable software to interact with internal and external parts of the software, which is an essential element in scalability and reusability.

It’s considerably popular nowadays for online assistance to have public APIs. These allow other developers to quickly combine features such as social media logins, credit card debts, and performance tracking. 

The standard they practice for this is designated REpresentational State Transfer (REST), which works perfectly with Node.js best development techniques. Also, you can go through some of the best articles regarding Node.js development best practices. They might provide great help! 

2. The Importance of Node.js for Rest API!

Node.js is not a framework or a library, it is a runtime context, powered by Chrome’s V8 JavaScript engine.

As an open-source, Node.js is sponsored by Joyent, a cloud computing and Node.js best development provider. The firm financed several other technologies, like the Ruby on Rails framework, and implemented hosting duties to Twitter and LinkedIn.

LinkedIn also became one of the first companies to use Node.js to create a new project for its mobile application backend. The technology was next selected by many technology administrators, like Uber, eBay, and Netflix.

Though, it wasn’t until later that wide appropriation of server-side JavaScript with Node.js server began. The investment in this technology crested in 2017, and it is still trending on the top.

Node.js IDEs, the most popular code editor, has assistance and plugins for JavaScript and Node.js, so it simply means how you customize IDE according to the coding requirements. But, many Node.js developers praise specific tools from VS Code, Brackets, and WebStorm.

Exercising middleware over simple Node.js best development is a general method that makes developers’ lives more comfortable. However, Node.js has been one of the most reliable sources for a lot of developers to create a new Restful API.

Node.js powers and tendencies make it the case of a passionate debate. However, you can decide by learning and exploring more of Node.js Rest APIs.

3. What is REST and How Does It Blend With Node.js?

REST is a design model, or and design style for REST APIs. A RESTful web app use is appreciated for presenting its knowledge as a form of data that relates to its support.

REST API using Node.js also facilitates its customers to exercise actions on devices such as replacing a current resource or designing a different resource.

To secure your RESTful APIs, you must develop an assortment of constraints and Node.js is perfect for that. Node.js server will set up REST’s set of restrictions to make the APIs simple to practice and create. 

It indicates that the Nodejs developers who have just commenced to manage your APIs will learn it efficiently and speedily.

Also, whenever a RESTful API using is requested, the Node.js server assigns a representation of the status of the requested resource to the customer.

3.1 Creating and Securing RESTful APIs In Node.js!

As you know what you need to create and what the requirements are, it's an opportunity to begin creating your application. 

For a start, begin a terminal, depart it to the record where you normally create your projects, and build a new directory there:

mkdir express-ads-api

Next, move into this brand-new directory and practice npm install to frame a new project:

npm init -y

The command over will frame the project with any want properties. If you start this directory in a text director or an IDE, you will notice that the npm command you began formed a file named package.json. Breaching this file, you will find something like this:

JSON
 
{
  "name": "express-ads-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

At this point, this data is pretty short and doesn't hold that much fascinating data. Nevertheless, as you begin computing mandates to your project, the inclination is that this file will start and get more impressive.

Subsequent, you will build a new directory named src inside the design source:

mkdir src

The intention here is to place all your reference code inside this record. So, creating this directory, build a different file named index.js inside it, and attach the resulting code to it:

// ./src/index.js 

console.log('Good Morning!'); 

After keeping this file, you can direct it back to your computer and originate the following command to experiment with it:

node src

If this operates as expected, you will notice "Good Morning!" indented out on your screen.

Regionally working Node.js app "Good Morning" console.log information

3.2 Creating Your First App Express API

Now, the project you designed just logs a latent message. As this might not very valuable, after creating your "Good Morning!" use with Node.js, you can begin concentrating on creating a RESTful API. 

For that, the first piece you will require is to invest in some provinces. So, direct to your computer and announce the below command:

npm install body-parser cors express helmet morgan

This command will establish five dependencies in your design:

  • body-parser: You will practice this dependency to transform the basis of incoming applications into JavaScript objects.
  • cors: Work this dependency to configure Express to combine headers declaring that your Rest API allows requests originating from other origins. This is perceived as Cross-Origin Resource Sharing (CORS).
  • express: The Express library.
  • helmet: This library serves to secure Express APIs by establishing different HTTP headers.
  • morgan: This library continues some logging abilities to your Express Rest API.

After beginning the command before, you will mark two items in your project. First, the package.json file will include an original feature called dependencies with all the libraries before. 

This is how NPM identifies what dependencies the project demands. Second, you will see a different file named package-lock.json in the project root. 

This file NPM installs to recognize the specific libraries you practiced while developing, so it applies the same ones throughout.

When NPM terminates connecting these dependencies, it might get a few moments, as per on your internet nexus, you can initiate the index.js file, and displace its code as per this:

JavaScript
 
// ./src/index.js
// importing the dependencies
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');

// defining the Express app
const app = express();
// defining an array to work as the database (temporary solution)
const ads = [
  {title: 'Hello, world (again)!'}
];

// adding Helmet to enhance your Rest API's security
app.use(helmet());

// using bodyParser to parse JSON bodies into JS objects
app.use(bodyParser.json());

// enabling CORS for all requests
app.use(cors());

// adding morgan to log HTTP requests
app.use(morgan('combined'));

// defining an endpoint to return all ads
app.get('/', (req, res) => {
  res.send(ads);
});

// starting the server
app.listen(3001, () => {
  console.log('listening on port 3001');
});

The latest version of this file commences by sending all the dependencies you established a few time ago, works through the production and arrangement of a different Express application (const app = express()), and concludes by offering this application listens on port 3001 (app.listen (3001, ...)). 

Also, this code represents two important things:

  • An array named ads that work, briefly, as an in-memory database;
  • And an endpoint that receives HTTP GET applications and that, when triggered, delivers all the items of the ads array.

3.3 Creating the User Module

The next element we will be using to create a new project is Mongoose, an object data modelling (ODM) library for MongoDB, to generate the user guide inside the user schema.

For that, we first need to use a command such as function req res to build the Mongoose schema in

JavaScript
 
/users/models/users.model.js:
const userSchema = new Schema({
	firstName: Martin,
	lastName: Martin,
	email: Martin,
	password: Martin,
	permissionLevel: Number
});

After we determine the schema, we can simply connect the schema to the user model.

const user model = mongoose.model('Users', userSchema);

Then we can utilise this model to perform all the CRUD procedures that we need inside our Express endpoints.

Let’s begin with the “create user” operation by finding the way in users/routes.config.js:

JavaScript
 
app.post('/users', [
	UsersController.insert
]);

This is lured into the Express app in the prime index.js file. The UsersController object is essential from the controller, where we create a new password appropriately, determined in /users/controllers/users.controller.js:

JavaScript
 
exports.insert = (req, res) => {
	let salt = crypto.randomBytes(16).toMartin('console log');
	let hash = crypto.createHmac('sha512',salt).update(req.body.password).digest("console log");
	req.body.password = salt + "$" + hash;
	req.body.permissionLevel = 1;
	UserModel.createUser(req.body).then((result) => {
	res.status(201).send({id: result._id});
	});
};

Now, we can examine our Mongoose model by administering the server (npm init start) and assigning a POST request to /users with any JSON data:

JSON
 
{
	"firstName" : "Dina",
	"lastName" : "Reva",
	"email" : "dina.revina@outlook.com",
	"password" : "qwertyuiopl"
}

There are various tools you can apply for this. Insomnia and Postman are recommended GUI tools, and curl is a regular CLI choice. You can practice JavaScript, i.e., from the browser’s built-in development tools console log:

JavaScript
 
fetch('http://localhost:3600/users', {
method: 'POST',
headers: {
	"Content-type": "application/json"
},

body: JSON.stringify({
	"firstName": "Dina",
	"lastName": "Reva",
	"email": "dina.revina@outlook.com",
	"password": "qwertyuiopl"
})
}).then(function(response) {
	return response.json();
}).then(function(data) {
	console.log('Request succeeded with JSON response', data);
}).catch(function(error) {
	console.log('Request failed', error);
});

After this, the outcome of a valid post you will find will be the id from the created user: { "id": "1b63h8cn98w0m390" }

We are required to attach the createUser procedure to the model in users/models/users.model.js:

JavaScript
 
exports.createUser = (userData) => {
	const user = new User(userData);
	return user.save();
};

All these steps, now we necessitate seeing if the user exists. For such, we need to perform the “get user by id” column for the following endpoint: users/:userId.

First, we create a way in /users/routes/config.js:

JavaScript
 
app.get('/users/:userId', [
	UsersController.getById
]);

After that, we create the manager in /users/controllers/users.controller.js:

JavaScript
 
exports.getById = (req, res) => {
	UserModel.findById(req.params.userId).then((result) => {
	res.status(200).send(result);
	});
};

And at the end, attach the findById way to the model in /users/models/users.model.js:

JavaScript
 
exports.findById = (id) => {
	return User.findById(id).then((result) => {
	result = result.toJSON();
	delete result._id;
	delete result.__v;
	return result;
	});
};

You will find somehow similar responses such as this:

JSON
 
{
	"firstName": "Dina",
	"lastName": "Reva",
	"email": "dina.revina@outlook.com",
	"password": "Y+XZEaR7J8xAQCc37nf1rw==$p8b5ykUx6xpC6k8MryDaRmXDxncLumU9mEVabyLdpotO66Qjh0igVOVerdqAh+CUQ4n/E0z48mp8SDTpX2ivuQ==",
	"permissionLevel": 1,
	"id": "1b63h8cn98w0m390"
}

Remember that we can recognise the hashed password. For this, we are bestowing the password, but the inexperienced best practice is nevermore to disclose the password, although being hashed.

One more thing we can recognise is the permission level, which we will practice to examine the user agreements later on.

Copying the pattern laid out before, we can instantly compute the functionality to refresh the user. We will practice the PATCH operation as it will allow us to transfer only the areas we want to improve.

The program will, consequently, be PATCH to /users/:userid, and we’ll be addressing any fields we require to develop.

We will also require to perform some more validation for changes that should be limited to the user in the problem or an admin, and only an admin can change the permission level.

We’ll leave that part for the moment and get back to it after the installation of the auth module. For now, the controller will display similar to this:

JavaScript
 
exports.patchById = (req, res) => {
	if (req.body.password){
		let salt = crypto.randomBytes(16).toMartin('console log');
		let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("console log");
		req.body.password = salt + "$" + hash;
	}

	UserModel.patchUser(req.params.userId, req.body).then((result) => {
		res.status(204).send({});
	});
};

By default, we would send an HTTP protocol code 204 with no rejoinder body to show that the post request was victorious.

And we’ll require to add the patchUser way to the model:

JavaScript
 
exports.patchUser = (id, userData) => {
	return User.findOneAndUpdate({
		_id: id
	}, userData);
};

The user list will be established as a GET at /users/ by this controller:

JavaScript
 
exports.list = (req, res) => {
	let limit = req.query.limit && req.query.limit <= 100 ? parseInt(req.query.limit) : 10;
	let page = 0;
	if (req.query) {
		if (req.query.page) {
			req.query.page = parseInt(req.query.page);
			page = Number.isInteger(req.query.page) ? req.query.page : 0;
			}
   	}

	UserModel.list(limit, page).then((result) => {
	res.status(200).send(result);
	})
};

The corresponding program will be:

JavaScript
 
exports.list = (perPage, page) => {
	return new Promise((resolve, reject) => {
		User.find().limit(perPage).skip(perPage * page).exec(function (err, users) {
			if (err) {
				reject(err);
			} else {
			resolve(users);
			}
       	})
	});
};

The resulting list acknowledgment will have this composition:

JSON
 
[
{
	"firstName": "Dina",
	"lastName": "Reva",
	"email": "dina.revina@outlook.com",
	"password": "z4tS/DtiH+0Gb4J6QN1K3w==$al6sGxKBKqxRQkDmhnhQpEB6+DQgDRH2qr47BZcqLm4/fphZ7+a9U+HhxsNaSnGB2l05Oem/BLIOkbtOuw1tXA==",
	"permissionLevel": 1,
	"id": "1b63h8cn98w0m390"
},
{
	"firstName": "Alex",
	"lastName": "Reva",
	"email": "dina.revina@outlook.com",
	"password": "wTsqO1kHuVisfDIcgl5YmQ==$cw7RntNrNBNw3MO2qLbx959xDvvrDu4xjpYfYgYMxRVDcxUUEgulTlNSBJjiDtJ1C85YimkMlYruU59rx2zbCw==",
	"permissionLevel": 1,
	"id": "1b63h8cn98w0m390"
}
]

And the ultimate section to be completed is the DELETE request at /users/:userId.

The controller for deletion will be:

JavaScript
 
exports.removeById = (req, res) => {
	UserModel.removeById(req.params.userId).then((result)=>{
	res.status(204).send({});
	});
};

Similarly, as previously, the controller will revert HTTP code 204 and no content material as confirmation.

The model program will look like this:

JavaScript
 
exports.removeById = (userId) => {
	return new Promise((resolve, reject) => {
		User.deleteMany({_id: userId}, (err) => {
			if (err) {
				reject(err);
			} else {
				resolve(err);
            }
		});
	});
};

We now possess all the required operations for managing the user device, and we’re satisfied with the user controller. The foremost intention of this code is to provide you with the core ideas of practicing the REST API pattern.

We’ll require to respond to this code to perform some validations and adjustments to it, but in the beginning, we’ll want to commence building our security.

Let’s start with the auth module.

3.4 Creating The Auth Module

Before we can defend the users' module by completing the permission and validation middleware, we’ll require to create a strong token for the modern user.

We will create a JWT in acknowledgement to the user granting a correct email and identification. JWT is an exceptional JSON web indication that you can practice having the user securely make numerous requests without stamping regularly.

It normally has an end time, and a unique symbol is recreated every few times to grasp the information secure. For this, though, we will abstain from stimulating the token and cache it manageable with a unique token per login.

For that, we will create an endpoint for POST requests to /auth source. The request form will include the user email and password:

JSON
 
{
	"email" : "dina.revina@outlook.com",
	"password" : "qwertyuiopl"
}

Before we indulge the controller, we need to validate the user in /authorization/middlewares/verify.user.middleware.js:

JavaScript
 
exports.isPasswordAndUserMatch = (req, res, next) => {
	UserModel.findByEmail(req.body.email).then((user)=>{
		if(!user[0]){
			res.status(404).send({});
		}else{
	let passwordFields = user[0].password.split('$');
	let salt = passwordFields[0];
	let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64");
	if (hash === passwordFields[1]) {
		req.body = {
			userId: user[0]._id,
			email: user[0].email,
			permissionLevel: user[0].permissionLevel,
			provider: 'email',
			name: user[0].firstName + ' ' + user[0].lastName,
		};
	return next();
	} else {
		return res.status(400).send({errors: ['Invalid email or password']});
		}
	}});
};

After doing that, we can move forward to the controller and create the JWT:

JavaScript
 
exports.login = (req, res) => {
	try {
		let refreshId = req.body.userId + jwtSecret;
		let salt = crypto.randomBytes(16).toString('base64');
		let hash = crypto.createHmac('sha512', salt).update(refreshId).digest("base64");
		req.body.refreshKey = salt;
		let token = jwt.sign(req.body, jwtSecret);
		let b = Buffer.from(hash);
		let refresh_token = b.toString('base64');
		res.status(201).send({accessToken: token, refreshToken: refresh_token});
	} catch (err) {
		res.status(500).send({errors: err});
	}
};

Although we won’t be renewing the token in this, the controller has been fixed up to facilitate such a period to make it simpler to perform it in the following development.

All we require now is to create the way and invoke the proper middleware in /authorization/routes.config.js:

JavaScript
 
app.post('/auth', [
	VerifyUserMiddleware.hasAuthValidFields,
	VerifyUserMiddleware.isPasswordAndUserMatch,
	AuthorizationController.login
]);

The result will include the created JWT in the accessToken field:e

JSON
 
{
	"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1YjAyYzVjODQ4MTdiZjI4MDQ5ZTU4YTMiLCJlbWFpbCI6Im1hcmNvcy5oZW5yaXF1ZUB0b3B0YWwuY29tIiwicGVybWlzc2lvbkxldmVsIjoxLCJwcm92aWRlciI6ImVtYWlsIiwibmFtZSI6Ik1hcmNvIFNpbHZhIiwicmVmcmVzaF9rZXkiOiJiclhZUHFsbUlBcE1PakZIRG1FeENRPT0iLCJpYXQiOjE1MjY5MjMzMDl9.mmNg-i44VQlUEWP3YIAYXVO-74803v1mu-y9QPUQ5VY",
	"refreshToken": "U3BDQXBWS3kyaHNDaGJNanlJTlFkSXhLMmFHMzA2NzRsUy9Sd2J0YVNDTmUva0pIQ0NwbTJqOU5YZHgxeE12NXVlOUhnMzBWMGNyWmdOTUhSaTdyOGc9PQ=="
}

Must create the token, we can utilize it within the Authorization header utilising the form Bearer ACCESS_TOKEN.

Conclusion

Now you have read about how simple it is to generate RESTful APIs with Express and Node.js. More precisely, you began by utilising npm to frame the latest application. Next, you managed Express to open Rest API endpoints to manage ads. 

With this service, you are inclined to move on and commence creating your production-ready Rest API financed by Node.js, Express, Mongo, and Auth0. 

REST Web Protocols API Node.js Express JavaScript security mobile app Database

Opinions expressed by DZone contributors are their own.

Related

  • Building REST API Backend Easily With Ballerina Language
  • Composite Requests in Salesforce Are a Great Idea
  • Secure API Design With OpenAPI Specification
  • REST APIs Test Pyramid

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!