Setup NodeJS Environment
npm init
npm install <Install the packages required e.g express, nodemon, etc>
Common packages that are required for any standard application:
1)express
2)http
3)body-parser
Configure NodeJS Development Environment to restart automatically upon change
1)Open package.json
2)Insert below code in the “scripts” section:
“start”: “nodemon app.js”
3)Execute “npm start”
This will start your app.js through nodemon, which will restart the process in case any update is made to any file in the project.
Creating a Server using NodeJS
const http = require('http') const app = http.createServer() app.listen(3000, () => { console.log('NodeJS started listening on port 3000') })
How to import NodeJS Modules
const http = require('http') const https = require('https')
Built in NodeJS modules
Module | Description |
---|---|
assert | Provides a set of assertion tests |
buffer | To handle binary data |
child_process | To run a child process |
cluster | To split a single Node process into multiple processes |
crypto | To handle OpenSSL cryptographic functions |
dgram | Provides implementation of UDP datagram sockets |
dns | To do DNS lookups and name resolution functions |
domain | Deprecated. To handle unhandled errors |
events | To handle events |
fs | To handle the file system |
http | To make Node.js act as an HTTP server |
https | To make Node.js act as an HTTPS server. |
net | To create servers and clients |
os | Provides information about the operation system |
path | To handle file paths |
punycode | Deprecated. A character encoding scheme |
querystring | To handle URL query strings |
readline | To handle readable streams one line at the time |
stream | To handle streaming data |
string_decoder | To decode buffer objects into strings |
timers | To execute a function after a given number of milliseconds |
tls | To implement TLS and SSL protocols |
tty | Provides classes used by a text terminal |
url | To parse URL strings |
util | To access utility functions |
v8 | To access information about V8 (the JavaScript engine) |
vm | To compile JavaScript code in a virtual machine |
zlib | To compress or decompress files |
Using the express module
const express = require('express') app = new express() app.listen(3000, () => { console.log('NodeJS started listening on port 3000') })
How to read GET request parameters?
const http = require('http') const express = require('express') app = new express() app.listen(3000, () => { console.log('NodeJS started listening on port 3000') }) const bodyparser = require('body-parser') app.use(bodyparser.urlencoded({extended: true})) //This step is to accept x-www-form-urlencoded //data which is nothing but the data sent from HTML forms app.use(bodyparser.json()); //This step is required to accept JSON request from JavaScript fetch or using axis module. You need either one of the above 2 lines depending on the use case. app.get('/admin', (req, res) => { console.log('request parameters = '+req.query.name) res.end('Hello '+req.query.name) }) Note that the "name" in req.query.name should exactly match the name of the parameter provide in HTML Form.
How to read POST request body?
const express = require('express') app = new express() app.listen(3000, () => { console.log('NodeJS started listening on port 3000') }) const bodyparser = require('body-parser') app.use(bodyparser.urlencoded({extended: true})) //This step is to accept x-www-form-urlencoded //data which is nothing but the data sent from HTML forms app.post('/registerUser', (req, res) => { res.end('Hello '+req.body.name)//name is a form field that //should be passed by the user })
Comparison among req.params, req.query and req.body
Suppose you are fetching a request using React’s axios module as below.
axios('http://localhost:4000/admin/adminPost/xdsdfwe3423432hjgh2b34b2?name=Animesh&job=self', { method: "POST", data: { name: 'animesh@gmail.com' } }) .then(res => { console.log("response received ..."); })
Below is how your backend NodeJS code looks like:
req.post('/admin/adminPost/:postID', (req, res, next) => { console.log("Request Params = ",req.params.postID); console.log("Request Query = ",req.query); console.log("Request Body = ",req.body.name); }, adminPost)
Below is the Output:
Request Params = xdsdfwe3423432hjgh2b34b2
Request Query = { name: ‘Animesh’, job: ‘self’ }
Request Body = animesh@gmail.com
Enable static path for the NodeJS project/app
Just use the below middleware to define the static path. This will enable access to any files under images or under any subdirectories under images without further definition of routes.
app.use('/images', express.static(path.join(__dirname,'images')))
Difference between module.exports and exports
module.exports can be used to export only one value from a file. It may be a string, and object or a function. If you have multiple module.exports in a file, only the last one will be exported.
On the other hand, exports can be used to export multiple objects, functions or strings from a JS file.
Below example demonstrates these 2 use cases, with the output.
NODEJS CODE app.js //module.exports can be used to export only one value. It may be a string, and object or a function. If you have multiple module.exports in a file, only the last one will be exported. console.log("********************* OUTPUT FROM module.exports *********************"); const stdName = require('./external/single') console.log("Name of the Student = "+stdName); console.log("**********************************************************************\n"); //exports can be used to export multiple items (objects, functions, strings etc. from the same js file) console.log("********************* OUTPUT FROM exports *********************"); const student = require('./external/multiple') console.log("Student1 = "+student.stdName1); console.log("Student2 = "+student.stdName2); console.log("Student3 = "+student.stdName3); console.log("Student4 = "+student.stdName4); console.log("getStudentClass function = "+student.getStdClass); console.log("***************************************************************"); single.js const studentName = "Animesh Banerjee" module.exports = studentName multiple.js exports.stdName1 = "Animesh" exports.stdName2 = "Aishani" exports.stdName3 = "Sudipto" exports.stdName4 = "Moumita" exports.getStdClass = () => { console.log("This is the getStdClass function provided by multiple.js"); } *********************************************************************************************** OUTPUT OUTPUT FROM module.exports Name of the Student = Animesh Banerjee OUTPUT FROM exports Student1 = Animesh Student2 = Aishani Student3 = Sudipto Student4 = Moumita getStudentClass function = () => { console.log("This is the getStdClass function provided by multiple.js"); }
Implementing the Router to segregate the requests
In MVC architecture, we have the views, controllers and models separated to make the code modular. In NodeJS, there is another component called Router, which is responsible for routing the requests to the respective controllers, based on the request URL and the request Method.
ExpressJS is the most common package that provides an easy way to implement this. Below is a very good starter code to structure your project according to this model:
app.js const bodyParser = require('body-parser'); const express = require('express') const path = require('path') const adminRouter = require('./routes/adminRouter') app = new express() app.use(bodyParser.urlencoded({extended: true})) app.use('/admin', adminRouter) app.listen(3000, () => { console.log("Server Listening on Port 3000 ...."); }) routes/adminRouter.js const adminPost = require("../controllers/adminPost"); const adminMain = require('../controllers/adminMain') const express = require('express') const router = express.Router() router.get('/main', adminMain) //NOTE : This represents a GET request with URL = http://<host>:3000/admin/adminMain router.post('/adminPost', adminPost) //NOTE : This represents a POST request with URL = http://<host>:3000/admin/adminPost module.exports = router //NOTE : The router is exported from here controllers/adminMain.js const adminMain = (req,res,next) => { console.log("Post Request received at admin controller ..."); console.log("Request Body Parameters Passed in Post Request of the adminMain controller = "+req.query.username); } module.exports = adminMain controllers/adminPost.js const adminPost = (req, res, next) => { console.log("Post Request received at /adminPost in the adminPost Controller"); console.log("Request Body Parameters Passed in Post Request of the adminPost controller = "+req.body.username); } module.exports = adminPost
Implement REST API using NodeJS
To create a REST API, you need to create some additional code along with setting up the Express JS, listener, router etc. Below is an example to create a Simple REST API that supports POST requests and returns a JSON response consisting of a “title” and a “content”.
app.js const bodyParser = require('body-parser'); const express = require('express') const adminRouter = require('./routes/adminRouter') app = new express() app.use(bodyParser.json()) //The below middleware is required to avoid CORS error, due to the requester and server domains being different, in case they are, which is mostly the case. app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*') res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, PUT, PUT') res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization') next() }) app.use('/admin', adminRouter) app.listen(4000, () => { console.log("Server Listening on Port 4000 ...."); }) routes/adminRouter.js const adminPost = require("../controllers/adminPost"); const express = require('express') const router = express.Router() router.post('/adminPost', adminPost) module.exports = router controllers/adminPost.js const adminPost = (req, res, next) => { console.log("Received the request with content = "+req.body.content); res.status(200).json({ title: 'Things to Explore in Singapore', content: 'The most exciting place in Singapore is the Sentosa. \ It is a small island with a lot of recreations around. There are \ amazing sculptures and beautiful beaches to relax and rejuvinate.' }) } module.exports = adminPost
Call REST API using axios module
axios is a third party module, that creates an Ajax call to get the response from a REST API. Below is the simple JavaScript code:
axios('http://localhost:4000/admin/adminPost', { 'name': 'Animesh', 'email': 'ani@gmail.com' }, { method: "POST" }) .then(res => { console.log("Response from axios call = ", JSON.stringify(res)); this.setState( state => ({ 'title' : res.data.title, 'content': res.data.content }) ) })
OUTPUT from the console.log
Call REST API using the normal JavaScript fetch()
fetch() is a JavaScript function that allows to make calls to any URL, and get the response in a Promise. It just takes the URL string as argument for GET request, whereas, for POST request, it takes the request BODY as a JS Object, along with the URL, as parameters. Below is an example for POST request. For GET, it works exactly the same way, but just remove the second parameter.
fetch('http://localhost:4000/admin/adminPost', { method: "POST" }) .then(res => { //This is the first promise which returns the raw response, that you need //to convert to json using the res.json() and return it for the next promise console.log("response = ", res); return res.json() }) .then(resData => { //In this promise, it returns the response data, where you can just //get any of the attribute values as resData.<attribute_name> console.log("resData = ", resData); this.setState( state => ({ 'title' : resData.title, 'content': resData.content }) ) })
ADD Authentication to REST APIs
There are few steps to implement authentication for REST API calls.
1) Client send a regular API call with username and password, which is already configured in the server for validation.
2) Server validates the credentials.
3) If credentials are correct, it creates a JWT Token (using the jsonwebtoken module’s sign method)
4) Server sends the JWT Token in the response body. It is passed in the body along with a key e.g. “Authorization”
5) Make sure that all the CORS headers have been set at the server side
6) Client receives the response with the “Authorization” key in the body. It stores the token in the localStorage of the browser. If you are using clients, other than browser, store the token in order to use it in the subsequent API calls.
7) In the next call, Client extracts the token from the browser localStorage, or from wherever it is stored in Step 6, and pass that as the value of the “Authorization” header in the request to call the REST service
8) When server receives the request with “Authorization” header, it extracts and validates the token using the jsonwebtoken module’s verify method.
9) If validation succeeds, server sends the response with the right data, otherwise sends back an error.
Server Side Code
======================================================================================= app.js ======================================================================================= const bodyParser = require('body-parser'); const express = require('express') const adminRouter = require('./routes/adminRouter') const checkJWT = require('./controllers/checkJWT') const { body } = require('express-validator') const checkIDPassword = require('./controllers/checkIDPassword'); app = new express() app.use(bodyParser.json()) app.use(bodyParser.urlencoded({extended: true})) //Setting the CORS headers app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*') res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, PUT, PUT') res.setHeader('Access-Control-Allow-Headers', '*') next() }) //This defines the API to login with username/password and generate the token for subsequent //calls app.post('/login', [ body('username') .trim() .isEmpty(), body('password') .trim() .isEmpty() ], checkIDPassword) //API call that requires the token to be present in the request header in the Authorization app.use('/admin', checkJWT, adminRouter) //This is for error handling, which you can customize in your own way app.use((error, req, res, next) => { if (error.statusCode) { console.log("Error in serving request. Status Code = ", error.statusCode, "Description = ", error.description); res.status(error.statusCode).json({ errorCode: error.statusCode, errorDescription: error.description }) } }) app.listen(4000, () => { console.log("Server Listening on Port 4000 ...."); }) ======================================================================================= checkIDPassword.js ======================================================================================= const { validationResult } = require('express-validator') const jwt = require('jsonwebtoken') const checkIDPassword = (req, res, next) => { //Validation whether username and password is present in the request const valErrors = validationResult(req) if (!valErrors.isEmpty()) { const error = new Error("Please provide Username & Password as part of the request body") next(error) } const username = req.body.username const password = req.body.password if (password === "P@ssw0rd") { //Generating the token, if username/password is correct const authToken = jwt.sign( { 'username': username }, 'mysecret-sdfsfsfs9398728hsdiufhwe78rywihuehf', { expiresIn: '1h' } ) //Adding the Bearer prefix (which is a common practice), and sending the response after //attaching the token to the Authorization header and body const bearerToken = 'Bearer ' + authToken res.setHeader('Authorization', bearerToken) res.status(200).json({ message: "JWT set successfully in the response header .......", Authorization: bearerToken }) } else { const error = new Error('User provided wrong credentials!!') throw error } } module.exports = checkIDPassword ======================================================================================= checkJWT.js ======================================================================================= const jwt = require('jsonwebtoken') const checkJWT = (req, res, next) => { console.log("req.method 1 == ", req.method); //As per CORS specifications, any POST call is preceded by an OPTIONS call to check if the //resource can be accessed. If the OPTIONS calls fails, then the actual POST call automatically //fails. However, since the OPTIONS call will not have the Authorization header, it is //important to check that and return with success. if (req.method == "OPTIONS") { next() } else { //Extracting the Authorization Header const authHeader = req.get('Authorization') if (!authHeader) { const error = new Error('Client Not Authorized to access the service!! '+authHeader) error.statusCode = 401 next(error) } //Separating the Bearer string from the token const authToken = authHeader.split(' ')[1] if (!authToken) { console.log("req.method 3 == ", req.method); const error = new Error("Client Not Authorized to access the service!!") error.statusCode = 401 next(error) } //The token verification is being done here jwt.verify(authToken, 'mysecret-sdfsfsfs9398728hsdiufhwe78rywihuehf', (error, decodedToken) => { if (error) { console.log("req.method 4 == ", req.method); const error = new Error('Token Validation Failed !!') error.statusCode = 401 next(error) } console.log("req.method 5 == ", req.method); next() }) } } module.exports = checkJWT ======================================================================================= adminMain.js ======================================================================================= const adminMain = (req,res,next) => { res.status(200).json({ name: 'Animesh Banerjee', age: 38, location: 'Singapore', familyMembers: [ { name: 'Moumita Banerjee', age: 34, relation: 'Spouse' }, { name: 'Aishani Banerjee', age: 9, relation: 'Daughter' }, { name: 'Sudipto Banerjee', age: 3, relation: 'Son' }, { name: 'Laxmi Kanta Banerjee', age: 67, relation: 'Father' } ] }) } module.exports = adminMain
Client Side Code (React)
======================================================================================= app.js ======================================================================================= import axios from 'axios' import { Component } from 'react'; class App extends Component { state = { loginResponse: '', serverResponse: '', authToken: '', tokenRemoveResponse: '' } //Remove Token from Browser localStorage removeToken = () => { localStorage.removeItem('TESTTOKEN') this.setState({tokenRemoveResponse: 'Token Removed!!'}) } getResponse = (url) => { axios.post(url, { 'username': 'sdfsdfsfs', 'password': 'P@ssw0rd' }, { method: 'GET', headers: { Authorization: localStorage.getItem('TESTTOKEN') //Get the token from //localStorage } }) .then(response => { //Checks the request URL. if it is for login, then it expects the token in the response body, //whereas if it is for the API call, it expects a the API response with data. if (url.includes('login')) { localStorage.setItem('TESTTOKEN', response.data.Authorization) //Set the //token in browser localStorage this.setState({loginResponse: "Login Successful", authToken: response.data.Authorization}) } else if(url.includes('main')) { this.setState({serverResponse: JSON.stringify(response.data)}) } else { this.setState({serverResponse: "Please provide a correct request url"}) } } ) .catch((error) => { console.log("ERROR == ", error) }) } render() { return( <div> <button style={{textDecoration: 'none', backgroundColor: 'brown', color: 'white'}} onClick={() => this.getResponse('http://localhost:4000/login')}>Login</button> <p>{this.state.loginResponse}</p> <hr/> <button referrerPolicy='no-referrer' style={{textDecoration: 'none', backgroundColor: 'brown', color: 'white'}} onClick={() => this.getResponse('http://localhost:4000/admin/main')}>Main Request</button> <p>{this.state.serverResponse}</p> <hr/> <button referrerPolicy='no-referrer' style={{textDecoration: 'none', backgroundColor: 'brown', color: 'white'}} onClick={this.removeToken}>Remove Token</button> <p>{this.state.tokenRemoveResponse}</p> </div> ) } } export default App;
This is how the Client looks like
Add Server side validation
You can use the express-validator module to incorporate validation at the server side in NodeJS.
Server Side Code app.js const bodyParser = require('body-parser'); const express = require('express') const adminRouter = require('./routes/adminRouter') const { body } = require('express-validator') app = new express() app.use(bodyParser.json()) app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*') res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, PUT, PUT') res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization') next() }) app.use('/admin', [ body('name') .trim() .isEmail() ], adminRouter) app.listen(4000, () => { console.log("Server Listening on Port 4000 ...."); }) routes/adminRouter.js const adminPost = require("../controllers/adminPost"); const express = require('express') const router = express.Router() router.post('/adminPost', adminPost) module.exports = router controllers/adminPost.js const { validationResult } = require('express-validator') const adminPost = (req, res, next) => { const errors = validationResult(req) if (!errors.isEmpty()) { res.status(422).json({ //422 is the normal response code for validation errors message: "The user entered an invalid Email!!" }) } console.log("Received the request with content = "+req.body.name); res.status(200).json({ title: 'Things to Explore in Singapore', content: 'The most exciting place in Singapore is the Sentosa. \ It is a small island with a lot of recreations around. There are \ amazing sculptures and beautiful beaches to relax and rejuvinate.' }) } module.exports = adminPost Client Side [Below is a code written on React to update the State with the error message] axios('http://localhost:4000/admin/adminPost', { method: "POST", data: { name: 'Animesh Banerjee' } }) .then(res => { console.log("response status is 200 ..."); this.setState( state => ({ 'hasError': false, 'title' : res.data.title, 'content': res.data.content }) ) }).catch((error) => { console.log("response status is not 200 ...", error.response); this.setState( state => ({ 'hasError': true, 'message' : error.response.data.message, 'content': '' }) ) }) //NOTE : You can get the response inside the catch block using the error.response object, then you can call error.response.data.message to get the exact string, which was set at the Server Side Code in the controllers/adminPost.js
Working with MongoDB, Mongoose and NodejS Models
MongoDB is a document based Database that comes with the Mongoose module for NodeJS, and is very easy to integrate with NodeJS. It has become extremely popular for the ease of use, and its flexibility. Below are the main steps for integration:
1)Create a DB Cluster, Database and a Collection in mongoDB Atlas. You can use a local installation as well, but using the Cloud Based solution is the easiest. It provides free storage upto 512 MB, which is really good for testing and learning.
2)Install mongoose package in the NodeJS Project
npm install mongoose
3)Create the Model in the NodeJS Project
models/Student.js const mongoose = require('mongoose') const studentSchema = mongoose.Schema({ name: { type: String, required: true }, class: { type: String, required: true }, school: { type: String, required: true } }, {timestamps: true}) //The timestamps object will insert the createdAt and updatedAt into the DB for each entry automatically module.exports = mongoose.model('Student', studentSchema)
4)Get the Connection URL from the mongoDB Atlas. Note that you need to allow connection from you Client IP [Network Access tab in Atlas Dashboard]. Click on the Connect button on your Cluster to get the Connection URL.
5)Create the Connection to MongoDB in app.js with the previous URL in Step 4
const mongoose = require('mongoose') mongoose.connect('mongodb+srv://<yourusername>:<yourpassword>@cluster0.bgyrx.mongodb.net/<yourDBName>?retryWrites=true&w=majority') .then(result => { app.listen(4000, () => { console.log("Server Listening on Port 4000 ...."); }) }) .catch(error => { console.log("Some problem happened in the DB Connection : ", error); })
6)Create the JavaScript Object as per the Schema defined in the Model
const StudentModel = require('../models/Student') const student = new StudentModel({ name: 'Aishani', class: '4', school: 'DPS' })
7)Call save(), update() or remove() operations on the Object that you got from Step 6 to create a new Object, update an existing one or to remove one from the Database respectively.
student.save() .then(() => { res.status(200).json({ result: 'Student Created Successfully' }) }) .catch(error => { console.log('Problem Happened in Creating Student ',error); })
There are several other important functions exposed by mongoose Model, that can be very helpful in working with the MongoDB.
Model.deleteMany()
Model.deleteOne()
Model.find()
Model.findById()
Model.findByIdAndDelete()
Model.findByIdAndRemove()
Model.findByIdAndUpdate()
Model.findOne()
Model.findOneAndDelete()
Model.findOneAndRemove()
Model.findOneAndReplace()
Model.findOneAndUpdate()
Model.replaceOne()
Model.updateMany()
Model.updateOne()
Got to this link to get detailed explanation of each of these functions : https://mongoosejs.com/docs/queries.html
Pagination with MongoDB Data
When dealing with either displaying posts in your page, or several news articles, then pagination is a must, so that the user gets limited data per page, and has the flexibility to move to the next or previous page. This can be very easily done using mongoose Models. Consider the below Models and variables already created.
Post – Model
totalItems – Total number of posts
currentPage – Get the current page from request parameter
perPage – Number of posts/items to show or paginate per page
Post.find() .countDocuments() .then(count => { totalItems = count; return Post.find() .skip((currentPage - 1) * perPage) .limit(perPage) })
Best Practice for Error Handling
It is always recommended that you don’t throw error in all the JS files. Instead, just use all the JS files to set a statusCode and a errorMessage/errorDescription to the Error Object, that you need to create and pass it as an argument to the next(error) call. This will take the control back to the calling script, and ultimately to app.js.
if (!errors.isEmpty()) { const error = new Error('Validation Error Happened!!') error.statusCode = 422 error.description = "Validation Error Description ...." next(error) }
In app.js, write a generic route at the bottom of all the routes(that will match all URLs), and check whether the statusCode is empty. If not empty, then set the statusCode as received, along with a json response with the statusCode and errorMessage set as a Javascript Object.
app.use('/', (error, req, res, next) => { //write this at the bottom of the app.js file, so //that any other routes are handled normally. In case the control comes to this route due to //the next(error) call, then set the response Code here, and return the json response //accordingly. if (error.statusCode) { console.log("Error in serving request. Status Code = ", error.statusCode, "Description = ", error.description); res.status(error.statusCode).json({ errorCode: error.statusCode, errorDescription: error.description }) } else { res.status(500).json({ //The control will come here, if somewhere in other scripts, you //have forgotten to set the status code, but just forwarded the error with next(error) errorCode: 500, errorDescription: "Some Error happened at Server" }) } })
Configure NodeJS for allowing file upload
1) Download the multer npm package
npm install multer
2) import the multer and path package
const multer = require('multer') const path = require('path')
3) Configure the destination and filename to store the file in server
var storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, path.join(__dirname,'../images')) //cb is the callback method | null means no //error }, filename: function (req, file, cb) { const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9) cb(null, file.fieldname + '-' + uniqueSuffix + '.jpg') //cb is the callback method | null //means no error } }) var upload = multer({ storage: storage })
4) Create the middleware in the router or express app variable, to extract the file from the request
router.post('/adminUpload', adminUpload, upload.single('profilePhoto')) // Here profilePhoto is //the fieldname from the input form, in which the file is passed.
Use the ‘path’ module to construct platform independent local paths
const express = require('express') app = new express() app.listen(3000, () => { console.log('NodeJS started listening on port 3000') }) const path = require('path') app.get('/', (req, res) => { res.sendFile( path.join(__dirname,'/index.html')) }) //Here __dirname is a special variable that denotes the path //of the current file from which this code is invoked.
Fetch JSON Data and store into MongoDB using NodeJS
Step1. Install the module node-fetch : npm install node-fetch
Step2. Use the below code:
const fetch = require('node-fetch') const mongoose = require('mongoose') const TaxiStand = require('./models/TaxiStand') mongoose.connect("mongo_URL") .then(result => { fetch('http://datamall2.mytransport.sg/ltaodataservice/TaxiStands', { method: "GET", headers: { "AccountKey" : "6S70dxfYT4CyKHJEdwco2g==" } }) .then(res => { return res.json() }) .then(resData => { let resDataArr = resData["value"] resDataArr.forEach(taxiStand => { let standCode = taxiStand["TaxiCode"] let latitude = taxiStand["Latitude"] let longitude = taxiStand["Longitude"] let hasBarrier = taxiStand["Bfa"] let standName = taxiStand["Name"] let standType = taxiStand["Type"] new TaxiStand({ standName : standName, standType : standType, standCode : standCode, hasBarrier : hasBarrier, latitude : latitude, longitude : longitude }).save() }); }) }).catch(error => { console.log("Some problem happened in the DB Connection : ", error); })
How to return a promise from an async function
If you are making a DB call, it will take some time to return the response. In that case, the function has to be handled differently. Let’s say below function is trying to create a customer in MongoDB.
const saveNewCustomer = (req, res) => { console.log("request body = ", req.body.custName); const name = req.body.custName const store = req.body.storeName const addr = req.body.address const phone = req.body.phoneNumber saveNewCustomerToDB(name, store, addr, phone) } const saveNewCustomerToDB = (custName, storeName, address, phoneNumber) => { const customer = new Customer({ 'custName': custName, 'storeName': storeName, 'address': address, 'phoneNumber': phoneNumber }) customer.save() .then(() => { console.log("New customer created successfully"); }) .catch(error => { console.log('Problem Happened in Creating Customer ',error); }) }
The problem with above logic is that you cannot get the result of DB update/save in the calling function i.e in saveNewCustomer function, since the customer.save() is an async function and will immediately return, but continue doing the save() asynchronously. So the calling function i.e saveNewCustomer will never get the result of the save().
To deal with such scenario, you need to define the saveNewCustomerToDB as an async function, and add await keyword while calling the customer.save() and return a Resolved Promise or a Rejected Promise, as is the case, from the then() and catch() functions respectively.
Here is the code for reference.
const saveNewCustomer = (req, res) => { console.log("request body = ", req.body.custName); const name = req.body.custName const store = req.body.storeName const addr = req.body.address const phone = req.body.phoneNumber saveNewCustomerToDB(name, store, addr, phone) .then(() => { res.status(200).json({ result: 'Customer Created Successfully' }) }) .catch((error) => { res.status(500).json({ result: 'Could not Create Customer'+error }) }) } const saveNewCustomerToDB = async (custName, storeName, address, phoneNumber) => { const customer = new Customer({ 'custName': custName, 'storeName': storeName, 'address': address, 'phoneNumber': phoneNumber }) console.log("Before await in the saveNewCustomerToDB ....."); await customer.save() .then(() => { console.log("New customer created successfully... Now it result will be returned as null"); return Promise.resolve() }) .catch(error => { console.log('Problem Happened in Creating Customer ',error); return Promise.reject(error) }) }
Handle a Promise from DB calls
If you call a mongoDB, it takes some time to return the response. This is how you get the response or error in the calling function.
//Calling Function const findCustomer = (req, res) => { console.log("phone number=",req.body.phoneNumber); findCustomerFromDB(req.body.phoneNumber) .then((output) => { //This is where you get the response res.status(200).json({ 'customers': output }) }) .catch(error => { res.status(404).json({ message: 'No Customer Found '+error }) }) } //Called Function which is returning the promise with an async/await const findCustomerFromDB = async (phoneNumber) => { return await Customer.find({ phoneNumber: phoneNumber }).exec() }