Friday, September 2, 2022

Build Node.JS RESTful APIs with MongoDB in Simple Steps

 

What is RESTful API?

REST is an acronym for Representational State Transfer. A REST API (also known as RESTful API) is an application programming interface (API or web API) that conforms to the constraints of REST architectural style and allows for interaction with RESTful web services.

What's an API?

An API is a set of definitions and protocols for building and integrating application software. It’s sometimes referred to as a contract between an information provider and an information user—establishing the content required from the consumer (the call) and the content required by the producer (the response). 

In other words, if you want to interact with a computer or system to retrieve information or perform a function, an API helps you communicate what you want to that system so it can understand and fulfill the request. 

You can think of an API as a mediator between the users or clients and the resources or web services they want to get. It’s also a way for an organization to share resources and information while maintaining security, control, and authentication—determining who gets access to what. 

Working:

A request is sent from client to server in the form of a web URL as HTTP GET or POST or PUT or DELETE request. After that, a response comes back from the server in the form of a resource which can be anything like HTML, XML, Image, or JSON. But now JSON(JavaScript Object Notation) is the most popular format being used in Web Services. 


In this tutorial, we will learn how to create a RESTful API using  Node.JS.

Tools:

  • Node.JS
  • MongoDB
  • Text Editor (Visual Studio Code, Sublime, Atom, etc.)
  • PostMan

Getting Started

For the purpose of this tutorial, I'll create RESTful APIs. To achieve this, I'll use the above-mentioned tools. 

Building a Node.JS RESTful APIs is a four-step Process. Following are the steps given below to build Node.JS RESTful APIs.

Step 1: Create a Folder Name say todolist_mongodb. You can give whatever name you would like to your folder. Get inside your created folder and then run the init command.

  • mkdir todolist_mongodb
  • cd todolist_mongodb
  • npm init -y

After doing these steps. Your directory should look something like this

|_todolist_mongodb
|__package.json

The "package.json"  file may consist of all the project settings and other npm package dependencies. If you open that file it should look like this

{
  "name": "todolist_mongodb",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Step 2: Create a "server.js" file. In this file, we'll create our server.

Inside that same folder, we'll create 3 more folders named "Controller", "Routes", and "Models".

After doing this step, your directory will look like this


Step 3: Installing npm modules for Server

At this point, let's set up our server. For which, I'm using express, nodemon and body-parser. I'm also using nodemon module which helps develop Node.js based applications by automatically restarting the node application when file changes in the directory are detected. For this purpose, I'm using the npm command

npm install express body-parser --save

npm install nodemon --save-dev

After installing these packages your "package.json" file will look like this. I've added a start script in our "package.json" file. Now instead of running node server.js, now I'll run the npm start command to start my server.

{
  "name": "todolist_mongodb",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.20.0",
    "express": "^4.18.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.19"
  }
}

Step 3: Server Setup

For setting up the server, open your "server.js" file, and let's make some changes to that file.

const express = require('express');
const bodyParser = require('body-parser');
const PORT = process.env.PORT || 3000;

const app = express();

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

app.listen(PORT, () => {
    console.log(`Server is running at ${PORT} port`);
});

After setting up your server, when you start the server using the npm start command this will produce the following output













CONGRATULATIONS.....You've successfully set up your SERVER file. Let's move to the next part of creating the model.

Step 4: Installing Mongoose

As I already told you that I'm using MongoDB as my DataBase for which we've to install the mongoose module.

What is Mongoose??

Mongoose is JavaScript Object-Oriented Programming library that helps users to create a connection between MongoDB and the Node.JS Javascript runtime environment.

Let's install mongoose using the following command in our project.

npm install mongoose --save

 After installing this package, your "package.json" file has a new module namely mongoose under the dependencies section.


Step 4: Connecting MongoDB

After successful installation of the mongoose module. Let's connect MongoDB with our server. Let's make some changes to that file for connecting the server with the database. I've created a database name "tasklistapp" which may contain all the collections which we'll make into our database.

const DB_URL = 'mongodb://localhost:27017/tasklistapp';
const mongoose = require('mongoose');

mongoose.connect(DB_URL, (err, result) => {
    if(err) {
        console.log("Error");
        throw err;
    }
    console.log("DataBase Connected !!");
});

After making that file. Let's import that file into our "server.js" file.













After successful import of the database file, our project may console the above messages which are as follows

Server is running at 3000 port

DataBase Connected !!

Step 5: Setting up the Schema

After a successful connection with MongoDB. Let's set up the schema.

For setting up the schema I'll create a new folder under the Models folder namely Todo. This folder may contain two(2) different files first for the model schema namely Task and the second for its services section namely services which we'll discuss after some time. But first, let's open the schema file ie., the Task file.

The task is a collection that may contain different documents of that schema.

Let's make some changes under the todo schema file i.e., Task.js

const mongoose = require('mongoose'); const taskSchema = new mongoose.Schema({ priority: { type: Number, required: true, unique: true }, taskName: { type: String, required: true }, completed: { type: Boolean, required: true, default: false } }); module.exports = mongoose.model('task', taskSchema);


Step 5: Setting up the Routes

After the Creation of Schema, Let's move forward to API Routes creation.

Before making the routes. Let's discuss something about the Routes and their methods.

Routing defines the way in which the client requests are handled by the application endpoints.

Routing method is derived from one of the HTTP methods and is attached to an instance of the express class.

Express supports methods that correspond to all HTTP request methods: get, post, and so on. For a full list. For more, you can visit expressApp.Methods

For the API Routes, I've created an "index.js" file under the Routes folder which we've previously created.

After creating that file. Let's import that file into our "server.js" file. After importing the "index.js" file of the Routes folder our "server.js" file will look like this

const express = require('express');
const bodyParser = require('body-parser');
const PORT = process.env.PORT || 3000;
const db = require('./DataBase');
const publicRoute = require('./Routes')

const app = express();

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

app.use('/api', publicRoute);

app.listen(PORT, () => {
    console.log(`Server is running at ${PORT} port`);
});



After importing the "index.js" file from the Routes folder. Let's make some API routes into that file.

For every route, there will be a controller function which we'll import from the "index.js" file from the Controller folder.

const express = require('express'); const router = express.Router(); const Controller = require('../Controller'); router.get('/home', Controller.homecontroller); router.post('/createtask', Controller.createTaskController); router.get('/gettask', Controller.fetchAllTaskController); router.delete('/removetask/:taskId', Controller.removeTaskByTaskIdController); router.patch('/updatetask', Controller.updateTaskController); router.patch('/changestatus', Controller.changeTodoStatus); module.exports = router;

As I've attached the Controller index file to our routes index file. Let's make some changes to Controller's index file.

const Services = require('../Models/Todo/services'); const homecontroller = async (req, res, next) => { try { res.status(200).send('Welcome to Home Page Controller !!'); } catch (error) { next(error); } }; const createTaskController = async (req, res, next) => { try { const isPriorityExist = await Services.checkIsPriorityExist(req.body.priority); if (isPriorityExist) { return res.status(200).json({ error: true, message: 'Same Priority Already Exists!!' }); } let reqObj = { priority: req.body.priority, taskName: req.body.taskName, completed: req.body.completed }; const response = await Services.createNewTask(reqObj); if (response) { return res.status(201).json({ error: false, data: response, message: 'Task Created Successfully!!' }); } else { return res.status(200).json({ error: true, message: 'Unable to Create any Task!!' }); } } catch (error) { next(error); } }; const fetchAllTaskController = async (req, res, next) => { try { const response = await Services.fetchAllTask(); if (response.length) { res.status(200).json({ error: false, message: 'Available Task', data: response }); } else{ res.status(200).json({ error: false, message: 'No Task Available', data: response }); } } catch (error) { next(error); } }; const removeTaskByTaskIdController = async (req, res, next) => { try { const isTaskExist = await Services.checkIsTaskExist(req.params.taskId); if (!isTaskExist) { return res.status(200).json({ error: true, message: 'No Such Task Avaiable!!' }); } const response = await Services.removeTaskById(req.params.taskId); if (response) { return res.status(200).json({ error: false, message: 'Task Removed Successfully!!' }); } else { return res.status(200).json({ error: true, message: 'Error Occurred While Removing the Task!!' }); } } catch (error) { next(error); } }; const updateTaskController = async (req, res, next) => { try { const isTaskExist = await Services.checkIsTaskExist(req.body.todoId); if (!isTaskExist) { return res.status(200).json({ error: true, message: 'No Such Task Avaiable!!' }); } const isPriorityAvailable = await Services.checkIsPriorityAvailable(req.body); if (isPriorityAvailable) { return res.status(200).json({ error: true, message: 'Same Priority Already Exists!!' }); } const response = await Services.updateAvailableTask(req.body); if (response) { return res.status(200).json({ error: false, message: 'Task Updated Successfully!!' }); } else { return res.status(200).json({ error: true, message: 'Error Occurred While updating the Task!!' }); } } catch (error) { next(error); } }; const changeTodoStatus = async (req, res, next) => { try { const isTaskExist = await Services.checkIsTaskExist(req.body.todoId); if (!isTaskExist) { return res.status(200).json({ error: true, message: 'No Such Task Avaiable!!' }); } const response = await Services.updateStatus(req.body.todoId); if (response) { return res.status(200).json({ error: false, message: 'Todo Status Updated!!' }); } else { return res.status(200).json({ error: true, message: 'Error Occurred while Changing the Todo Status!!' }); } } catch (error) { next(error); } }; module.exports = { homecontroller, createTaskController, fetchAllTaskController, removeTaskByTaskIdController, updateTaskController, changeTodoStatus };


As I've used the Services file in the Controller. So, let's make some changes to that file too.

const task = require('./Task'); const createNewTask = async (reqObj) => { return await task.create(reqObj); }; const checkIsPriorityExist = async priority => { const isExist = await task.findOne({ priority: priority }); if (isExist) { return true; } else { return false; } }; const fetchAllTask = async () => { return await task.find().sort({ 'priority': 1 }); }; const checkIsTaskExist = async taskId => { const isTaskExist = await task.findOne({ '_id': taskId }); if (isTaskExist) { return true; } else { return false; } }; const removeTaskById = async taskId => { return await task.deleteOne({ '_id': taskId }); }; const checkIsPriorityAvailable = async body => { const isDataExist = await task.findOne({ '_id': body._id }); if ((!isDataExist) || (isDataExist._id.toString() == body._id.toString())) { return false; } return true; }; const updateAvailableTask = async body => { return await task.updateOne({ _id: body.todoId }, { taskName: body.taskName, priority: body.priority, completed: false }); }; const updateStatus = async todoId => { const isDataExist = await task.findOne({ '_id': todoId }); return await task.updateOne({ _id: todoId }, { completed: !isDataExist.completed }); } module.exports = { createNewTask, checkIsPriorityExist, fetchAllTask, checkIsTaskExist, removeTaskById, checkIsPriorityAvailable, updateAvailableTask, updateStatus };


Once you are done with that. CONGRATULATIONS.....You've successfully created all the APIs.

Testing via PostMan

Now that everything is now connected, let's test each of the routes as per their respective methods.
Once you open Postman or another application like the postman.
  1. http://localhost:3000/home is the URL for home page to check whether the APIs are hitting or not. Once you hit this path, you'll get "Welcome to Home Page Controller !!" as an output.

Adding a Middleware

After hitting all these above-created APIs, what happens if we entered the wrong route? Say, if you entered a route like "http://localhost:3000/xyz" or "http://localhost:3000/xyz/ajs". It responds with a message "Cannot GET /xyz or /xyz/ajs". Let's add an express middleware that could help us to return more interactive messages.

Middlewares basically intercept incoming HTTP requests and as such, you can use them to perform several operations ranging from authentication to validations, etc. You can visit Middleware for more info. 

To do this, open your "server.js" file and write the code snippet into it just before the "app.listen" line.

app.use(function(req, res) { res.status(404).send({url: req.originalUrl + ' not found'}) });

Just like that.













This snippet above helps you to redirect and respond whenever a wrong route is hitted on your server.


You can also visit my GitHub repository todolist_mongodb_blog for that code in case if you got any type of issue.

No comments:

Post a Comment