In this article we will learn how to implement CRUD in NodeJS using MongoDB as database. We will also learn how to build REST APIs for each CRUD operation and to use rest client in visual studio code to test those APIs. We will also learn to interact with mongo database with the help of MongoDB compass.

Checkout this complete source code on Implementing CRUD in NodeJS with Express and MongoDB here.

Prerequisites

We first require the following to be installed in our system to get started

  • Visual Studio Code
  • NodeJS
  • MongoDB
  • MongoDB compass

Setting Up NodeJS application with Express and MongoDB

Before jumping into CRUD operations lets first setup our app.

Run the following command in terminal to initialize the project.

npm init

This will create a package.json file in the folder.

Run the below command to install

  • express, a NodeJS framework with robust features
  • body-parser, to allow server to parse and read request body,
  • dotenv, to read environment variables from .env file
  • mongoose, a straight-forward schema-based solution to model application data
  • nodemon, which helps us run the NodeJS application without having to restart it every time we save any changes to our code.
npm i express body-parser mongoose dotenv nodemon

Now let’s create app.js file in our main folder which will be the entry point to the NodeJS application. The app.js file will contain the following code.

const express = require('express');
const mongoose = require('mongoose');
require('dotenv').config();
const port = process.env.PORT || "8000";

const app = express();

app.use(express.urlencoded({ extended: false }));
app.use(express.json());

const dbURI = process.env.DB_URI;

mongoose
	.connect(dbURI, {
		useNewUrlParser: true,
		useUnifiedTopology: true,
	})
	.then(() => console.log("Database Connected"))
	.catch((err) => console.log(err));

mongoose.Promise = global.Promise;

app.use('/', (req,res) => {
     res.send('Hello from express!!')
});

module.exports = app;

app.listen(port, () => {
    console.log(`Listening to requests on http://localhost:${port}`);
  });

Here we are first importing express, a instance of which is later assigned to a app variable. We are also importing mongoose with which we are connecting to our mongo database. We are reading mongoDb URI and PORT number from the .env file with the help of dotenv so go head and create a file named .env in root folder which will contain the following code

PORT = 8000
DB_URI = mongodb://localhost:27017

We are using port 8000 to run our NodeJs app locally and port 27017 for database, which is a default port used by mongo.

Lets now run our app using the below command

nodemon app

Now you should see the following messages in your terminal

nodemon start

Let’s test if our express app is working by hitting http://localhost:8000 in browser.

express hello

As you can see we are getting desired result from our express app in browser.

What is CRUD?

CRUD stands for Create, Read, Update, Delete as an acronym for ways we can operate on data stored in database.

Create operation is usually used for adding a new document in the database. An example of this would be, registering a new user. Read is used when we want to extract information from database, for example reading the information of the registered user. Update, as the name suggests will be used in case of making alterations to documents, for example changing password for a user. Delete, is basically used to remove the document from database.

Creating new document in MongoDB using NodeJS

In order to create a new document in database let’s take a example of registering a new user to our application. We first need to create a model for that user based on schema.

Create a folder named models and a file named users.model.js inside it. Code will be as follows

const mongoose = require('mongoose')

var userSchema = new mongoose.Schema({
    username: {
        type : String,
        unique : true,
        required : true
    },
    nickname:{
        type : String,
    },
    password: {
        type: String,
        required: true
    }
})

mongoose.model('User',userSchema)

Here we are using mongoose to create user schema containing username, nickname and password.

Now we want to redirect all the incoming requests to the specific routes, for that we will create another folder called routes with file index.js inside it. File will contain the below code

const express = require('express');
const router = express.Router();
const mongoose = require('mongoose')
const User = mongoose.model('User')

//CRUD Operations
router.post('/create', async (req,res)=>{
    try{
        const user = new User(req.body)
        const result = await User.create(user);
        res.send(`${result.username} successfully created!!`)
    }catch(err){
        res.status(500).send(err.message)
    }
})

module.exports = router

Here we are using router module of express to help us route our requests to respective paths. We are also importing the user model that we created above using mongoose.

For creating a user in database we have to use post request as we will get user credentials in request body. We have used ‘/create’ as a POST API endpoint which we will call along with user credentials to create a user. We are creating a new user with the help of user model, then we are using mongoose create method to add that user in database. On successful addition we are sending a response back with username.

In case of any errors we sending 500 status code with error message.

Create vs Save

There is another way of implementing the same using save method. Code will be as follows

router.post('/create', async (req,res)=>{
    try{
        const user = new User(req.body)
        const result = await user.save();
        res.send(`${result.username} successfully created!!`)
    }catch(err){
        res.status(500).send(err.message)
    }
})

As you can see we can either user save() or create(), both give the same result. Only difference is in the implementation, save() can only be used on a new object of the model while create() can be used on the model.

Hashing Password using Bcrypt

You will notice that we are saving the user credentials as it is, which means password also is getting saved as received in request body. This is not a ideal practice to follow. We should encrypt or hash the password before saving it to the database.

For this we can use bcrypt package in NodeJS. Let’s see how to hash the password using bcrypt.

const express = require('express');
const router = express.Router();
const mongoose = require('mongoose')
const User = mongoose.model('User')
const bcrypt = require('bcrypt')

//CRUD Operations
router.post('/create', async (req,res)=>{
    try{
        const user = new User(req.body)
        user.password = bcrypt.hashSync(user.password,bcrypt.genSaltSync(10))
        const result = await User.create(user);
        res.send(`${result.username} successfully created!!`)
    }catch(err){
        res.status(500).send(err.message)
    }
})

module.exports = router

Here we have imported bcrypt package which we are using to hash the password. Here bcrypt is hashing the received password with a salt, you can think of it as 10 random bytes, to generate a hashed password.

We need to change the following code in app.js file to send request to the routes defined in index.js file in route folder.

const usersRoute = require('./routes/index.js');

app.use('/', usersRoute);
Testing Create REST API

let’s test create API using rest client in visual studio code. If you don’t have it, please download it from vs code extensions.

To test, we need to create a file named crudTest.rest. Copy the following code to that file

POST http://localhost:8000/create
Content-Type: application/json

{
    "username": "codewithAzzan",
    "nickname":"Azzan",
    "password":"badPassword"
}

Here we are sending POST request to our API, http://localhost:8000/create with user credentials. After you hit send request option displayed just above first line, we will get the following result.

create response

As you can see we, we get a success response with success message form create API.

Let’s see if the user is added in data base.

Using MongoDB Compass to cross-verify

Go to MongoDB Compass. You will see the following screen.

compass Screen

Here by default it asks you to connect to the above URI. This is the same URI we configured to connect in our express app, so lets just hit connect. And navigate to users collection as shown below.

create mongo

As you can see the user is successfully added to our mongo database. We can also see that password is hashed and saved in database. Here _id is a unique key created by default for every record we add to the collection.

This is all about creating a record in mongo using NodeJS. Let’s move to read operation.

Reading documents in MongoDB using NodeJS

Let’s create a API for reading records from database. For reading we send a GET request to the API with request parameter to identify the record. Copy the below code to index.js in route folder.

router.get('/read/:username', async (req,res)=>{
    try{
        const username = req.params.username
        const result = await User.findOne({username});
        if(result)
            return res.send(result)
        else 
            return res.status(404).send(`${username} does not exist`)
    }catch(err){
        res.status(500).send(err)
    }
})

Here we have created a ‘/read/:username’ GET endpoint, where we are passing the username to find the record associated with it. First we are retrieving the username from request parameter then using a findOne method of mongoose to retrieve the record associated with it from database and passing it back as response. If user is not found we are sending 404 back in response with a message. In case of any other error, we are doing the same as we did above.

FindOne Vs Find

You will notice we are using findOne method, which as the name suggests will fetch only one record, also we have a unique constraint on username which means we will only have a single record for every username in database.

We can use find as well instead of findOne, it will give the same result. Only difference is that we use find to get one or more records matching the condition.

Let’s see we want to fetch all the users present in our database. Code will be as follows,

//Reading multiple records
router.get('/read', async (req,res)=>{
    try{
        const result = await User.find();
        return res.send(result)
    }catch(err){
        res.status(500).send(err)
    }
})

Here we just have ‘/read’ endpoint without any request parameter as we want to read the entire collection. Similar to findOne, here we have used find method on User to retrieve all the documents in User collection.

Testing Read APIs

Let’s test read APIs using rest client.

read op test

We see here that when we are hitting ‘http://localhost:8000/read/codewithAzzan’ where codewithAzzan is the username we are trying to fetch record for, and we are getting the entire record associated with that username.

read op 2

Here we are fetching all the records in database not just any particular record. And we get the list of all records available in database.

Updating documents in MongoDB using NodeJS

For updating let’s take an example of updating nickname in a particular record. Code will be as follows

router.put('/update', async (req,res)=>{
    try{
        const username = req.body.username
        const newNickName = req.body.nickname
        const result = await User.findOneAndUpdate(
            { username: username },
            {
              $set: {
                nickname: newNickName,
              }
            },
            { new: true }
          );
        res.send(result)
    }catch(err){
        res.status(500).json(err.message)
    }
})

Here we are creating a PUT endpoint which will receive username against which we have to update the nickname. New nickname also we will get in request body. Then we are using findOneAndUpdate method of mongoose which will find the particular record in our case matching with the username we are providing then updates the nickname with new nickname. You will notice a key new set to true, that is used, so the query will return the updated new document.

If a matching document does not contain the nickname field, then the query will add the field and update its value.

Difference between findOneAndUpdate, updateOne, update and updateMany

We just used findOneAndUpdate and know how it works, similar to findOneAndUpdate we also have updateOne method, It also updates only the top most document in a collection with matching filter. Only difference between findOneAndUpdate and updateOne is that, updateOne does not return a document whereas findOneAndUpdate does.

UpdateMany will update all the documents with the matching condition. And update method by default will update only one document but if we include option { multi : true } then it will update all the documents matching the filter.

Testing Update API

Lets test the update API in rest client by changing the nickname for the username we created above.

Here we are hitting the update API with username and nickname in request body. And we get the updated document in response.

Let’s check in Mongo if the document is updated.

update proof

As you can see, the document is updated with new nickname.

Deleting documents in MongoDB using NodeJS

Let’s try to delete the user we created above. Code will be as follows.

router.delete('/delete', async (req,res)=>{
    try{
        const username = req.body.username
        const result = await User.deleteOne({username});
        res.send(result)
    }catch(err){
        res.status(500).json(err.message)
    }
})

Here we have created a DELETE endpoint which will receive username in request body, and we will delete the entire document matching that username.

Here we have used deleteOne method which only delete the first matching document in database.

deleteOne vs deleteMany

As the name suggests deleteOne deletes only singe document matching the filter whereas deleteMany will delete all the documents matching the filter.

Testing Delete API

Let’s test delete API by deleting the document associated with the username we created above.

delete op

Here we are triggering the delete API with username in request body and we get the success response with count of deleted documents.

This covers all the CRUD operations.

Summary

In this article we learned how to perform CRUD operations in MongoDB using Nodejs with express as framework. We also learned different methods to implement a particular CRUD operation. We learned how to test APIs using rest client in visual studio code and to interact with MongoDB using MongoDB Compass.

Learn about Implementing JWT Authentication in NodeJS here.

Leave a Reply

Your email address will not be published. Required fields are marked *