CRUD (Create, Read, Update, Delete) Operations on NoSQL Database (MongoDB) Using Node.js

Balakrishna V

7 min read

Hi everyone, I hope all are doing great!

In this blog, let us look into what is CRUD, the demonstration of the CRUD operations on MongoDB, and how the operations are performed in both Synchronous and Asynchronous ways. Basic knowledge of Node.js is appreciated.

NoSQL database used: MongoDB

Table of Contents:

  1. Introduction
  2. Setting up MongoDB (NoSQL)
  3. Synchronous Vs Asynchronous Execution
  4. Creating a Schema, Model, Object
  5. CREATE (Synchronous)
  6. CREATE (Asynchronous)
  7. READ (Synchronous)
  8. READ (Asynchronous)
  9. UPDATE (Synchronous)
  10. UPDATE (Asynchronous)
  11. DELETE (Synchronous)
  12. UPDATE (Asynchronous)
  13. Resources & References

1. Introduction

CRUD is an acronym for CREATE, READ, UPDATE & DELETE, which are rudimentary operations in the database.

In this post, I have used MongoDB as my primary database, which is a NoSQL database. Unlike the traditional row/column model, it saves the data aka document in a JSON format and performs the CRUD operations using the Node.js language.

Fundamentally Node.js works asynchronous or non-blocking by default so that it does not wait in cases where the CPU is used intensively by a code.

Thus designing CRUD operations as synchronous or asynchronous based on our case is a challenging process, particularly when we use it in a real-time server operation. In the following sections of the blog, I have explained on how I performed the CRUD operations in both sync/async ways.

I have given the example of a Movie Database throughout, which contains the details related to a specific movie so that you can manipulate it based on your use case.

2. Setting up the MongoDB (NoSQL)

MongoDB is a document database, which means it stores data in JSON-like documents. We believe this is the most natural way to think about data and is much more expressive & powerful than the traditional row/column model.

We need the below two main components to set up the MongoDB browser.

  1. MongoDB Community Server — Download here
  2. MongoDB Compass (GUI Viewer) — Download here
MongoDB Community Server
MongoDB Compass (GUI Viewer for MongoDB)

After installing both applications, we need to add the MongoDB server path to Environment Variables on your PC. So navigate yourself in the C directory of your PC and find the location of the mongod.exe file and copy the address path. In my case, I found the address to be in

C:\Program Files\MongoDB\Server\4.4\bin

Add the above location to environment variables like the one below.

Now, we are ready to create documents in our MongoDB database. Also, we can view the database in MongoDB Compass, after creating the first document.

MongoDB Compass document view. Source — Author

3. Synchronous Vs Asynchronous Execution

In Node.Js, we can perform database CRUD operations in both synchronous (blocking) and asynchronous (non-blocking) ways.

Sometimes while doing CRUD operations, we might need the variable to send via API or use it in the subsequent codes, etc., In that case, we use synchronous programming, where a line in the code waits for the previous line to finish before its execution.

This is a time-consuming process, so alternatively we can do the operations asynchronously. This is purely based on the use case that we are working on. We shall see the demonstration in both ways to perform the same CRUD operations.

4. Creating a Schema, Model, and Object

A Schema is like a blueprint specifying what to store in the database and in which format.

For taking this demonstration in a more effective and understandable way, as I said previously I will be creating a movie database that contains the movie properties such as,

  1. Movie name — (String)
  2. Director — (String)
  3. IMDB rating — (Number)
  4. Cast — (Array)
  5. Release date — (Datetime)
  6. Genre — (String)
  7. Sequel — (Boolean)

The primary reason for storing the above elements is to incorporate all kinds of data structures like String, Boolean, Number, Array, DateTime, etc.,

const mongoose = require('mongoose') // npm install --save mongoose
console.clear()

// Creating a Database
mongoose.connect('mongodb://localhost/MOVIELIBRARY', { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('Connected to MongoDB...'))
.catch(err => console.error('Could not connect to MongoDB...', err));

const movieSchema = new mongoose.Schema({
    movieName : String,
    director: String,
    imdbRating: Number,
    cast: [String],
    releaseDate: Date,
    genre: String,
    sequel: Boolean
});

// Creating an Class based on the Schema (blueprint / template)
const movieClass = mongoose.model('movieClass', movieSchema);

// Creating a new object (Instance of a class)
const tenet = new movieClass({
    movieName : "Tenet",
    director: "Christopher Nolan",
    imdbRating: 8,
    cast: ["George David Washington, Robert Pattinson"],
    releaseDate: new Date('2020-12-04'),
    genre: "science-fiction",
    sequel: false
})

const result = tenet.save();
console.log('Document created', result)

So in the code snippet above, I have created the movieSchema which contains the blueprint of the document. And later I create a constant movieClass which is a model that is created based on the schema.

Using the model, we can create an instance aka object used for a new entry in the document. It is very similar to the OOPS concept of class and objects.

And as you can see, I have created a new object called ‘tenet’ to resemble the film name, populated the fields, and later saved it. Now if we check the GUI database viewer — MongoDB Compass, we must see the new document created.

Now let’s proceed further with CRUD operations in both synchronous and asynchronous ways.

5. CREATE (Synchronous)

We have already created a new document named after ‘tenet’ previously. So now we create a function that can be used to create many movie listings hereafter.

const movieClass = require('./movieSchema')

// // CREATE
// Function Declaration
async function insertMovie(name_, director_, rating_, cast_, date_, genre_, sequel_){
    const movieObject = new movieClass({
        movieName : name_,
        director: director_,
        imdbRating: rating_,
        cast: cast_,
        releaseDate: date_,
        genre: genre_,
        sequel: sequel_
    });
    result = await movieObject.save() 
    return result
}

// Function Call
let output_1 = insertMovie('Inception', 'Christopher Nolan', 7, ['Leonardo DiCaprio', 'Cillian Murphy'], new Date('2010-07-16'),
                         'science-fiction', false)
output_1.then(function(response) {
    console.log(response, 'output response')
})

// Function Call
let output_2 = insertMovie('Tenet', 'Christopher Nolan', 7, ['George David Washington', 'Robert Pattinson'], new Date('2020-12-04'),
                         'science-fiction', false)
output_2.then(function(response) {
    console.log(response, 'output response')
})

// Function Call
let output_3 = insertMovie('The Conjuring', 'James Wan', 6, ['Vera Farmiga', 'Patrick Wilson'], new Date('2013-07-15'),
                         'supernatural horror', true)
output_3.then(function(response) {
    console.log(response, 'output response')
})

After the function insertMovie is declared, we can do a function call with its input parameters, which need to be updated in the database. Once they’re inserted, we will get the document that was inserted. Here we have inserted 3 movies.

6. CREATE (Asynchronous)

Similarly, we can insert new documents in the Mongo DB, without waiting or otherwise no code blocking.
Also, I have added another 3 movie sets using the no code-blocking way.

const movieClass = require('./movieSchema')

// // CREATE
var theDarkKnight = new movieClass({movieName : "The dark knight",
                            director: "Christopher Nolan",
                            imdbRating: 8,
                            cast: ["Christian Bale", "Heath Ledger"],
                            releaseDate: new Date('2008-01-14'),
                            genre: "superhero",
                            sequel: true})

console.log('DB Updated');
theDarkKnight.save()

var djangoUnchained = new movieClass({movieName : "Django Unchained",
                            director: "Quentin Tarantino",
                            imdbRating: 8,
                            cast: ["Christoph Waltz", "Jamie Foxx"],
                            releaseDate: new Date('2012-12-12'),
                            genre: "revisionist Western",
                            sequel: false})

console.log('DB Updated');
djangoUnchained.save()

var theNun = new movieClass({movieName : "The Nun",
                            director: "Corin Hardy",
                            imdbRating: 6,
                            cast: ["Taissa Farmiga", "Demián Bichir"],
                            releaseDate: new Date('2018-09-04'),
                            genre: "supernatural horror",
                            sequel: false})

console.log('DB Updated');
theNun.save()

7. READ (Synchronous)

Once we have created the documents, now we can filter them based on custom conditions and extract specific documents from MongoDB.

In the interests of brevity, I have created some functions using

  1. Fetch data by using a unique identifier
  2. Fetch data by using comparison operators like ( >, ≥, <, ≤, in, not in)
  3. Fetch data by using logical operators like (and, or)
  4. Fetch data by using the regex condition
  5. Fetch data and return the total count of documents
const movieClass = require('./movieSchema')

// // READ 
// Function Declaration - Variant 1
async function fetchMovies(){
    const movieObjects = await movieClass
        .find({sequel: false}) // Find movies with no sequel
        .limit(10) // Display upto 10 fields
        .sort({imdbRating: 1}) // Sort based on imdb rating
        .select({movieName:1, director:1} // Select only the fields specified here,  remove select to list all fields in the document
        ); 
    return movieObjects
}

// Function Call
let read_output_ = fetchMovies();
read_output_1.then(function(response) {
    console.log(response, 'output response')
})

// Function Declaration - Variant 3
async function fetchMovies_Comparision(){
    // eq (equal)
    // ne (not equal)
    // gt (greater than)
    // gte (greater than or equal to)
    // lt (less than)
    // lte (less than or equal to)
    // in
    // nin (not in)
    
    // Query here
    const movieObjects = await movieClass
        .find({sequel: { $eq: true }}) // Fetches the movies with sequel equal to true
        // .find({imdbRating : { $gte: 5, $lte: 9 }, sequel : { $eq: true}}) // Fetches the movies with imdb rating > 5 & imdb rating < 9, and sequel to be true
        // .find({imdbRating : { $in:[7,8,9] }}) // Fetches the movies with imdb rating with 7,8,9

        .limit(10) 
        .sort({imdbRating: 1})
        .select({movieName:1, director:1});
        return movieObjects
}

// Function Call
let read_output_2 = fetchMovies_Comparision();
read_output_2.then(function(response) {
    console.log(response, 'output response')
})

// Function Declaration - Variant 3
async function fetchMovies_Logical(){
    // or
    // and
    
    // Query here
    const movieObjects = await movieClass
        .find()
        // .find({ sequel: /^true/ }) // Regex - Self explanatory

        // .or([ {sequel: false}, {imdbRating: 8} ]) // Fetches the movie with sequel false or imdb rating = 8
        .and([ {sequel: false}, {imdbRating: 8} ]) // Fetches the movie with sequel false and imdb rating = 8

        .limit(10) 
        .sort({imdbRating: 1})

        return movieObjects
}

// Function Call
let read_output_3 = fetchMovies_Logical();
read_output_3.then(function(response) {
    console.log(response, 'output response')
})

// Function Declaration - Variant 4
async function fetchMovies_Regex(){    
    // Query here
    const movieObjects = await movieClass
        
        .find({ director: /^Quentin/ })         // Starts with Quentin 
        // .find({ director: /tarantino$/i })   // Ends with tarantino
        // .find({ director: /.*Nolan.*/i })    // Contains Nolan
        
        .limit(10) 
        .sort({conversationId: 1})
        .select({}); // Select all
        return movieObjects
}

// Function Call
let read_output_4 = fetchMovies_Regex();
read_output_4.then(function(response) {
    console.log(response, 'output response')
})

// Function Declaration - Variant 5
async function fetchMovies_Count(){
    const movieObjects = await movieClass
        // .find({sequel: false, imdbRating : 8})
        .find({sequel: false,})
        .limit(10)
        .sort({imdbRating: 1})
        .countDocuments();
    
        return movieObjects
}

// Function Call
let read_output_5 = fetchMovies_Count();
read_output_5.then(function(response) {
    console.log(response, 'output response')
})

8. READ (Asynchronous)

THE READ OPERATION IS STRICTLY SYNCHRONOUS

9. UPDATE (Synchronous)

There are always occurrences where we need to update the document in the future. So, in the below functions, I have added (append) new records to an array and also replaced the Boolean and Number with new values. We can use $push and $set array update operators reserved in MongoDB and you can try out all other operators.

You can also manipulate and create new functions based on the ones created below, to adhere to your use case.

const movieClass = require('./movieSchema')

// // UPDATE
// Update Array
async function update_MovieCast(movieName, newCast){

    const movieObjects = await movieClass.collection.findOneAndUpdate(
        {movieName: movieName}, 
        { 
            $push: {
                cast: `${newCast}`},
        }
        );
    return movieObjects
}

// Function Call
let update_output_1 = update_MovieCast('Inception', 'Michael Caine')
update_output_1.then(function(response) {
    console.log(response, 'output response')
})


// Update Boolean and Number
async function update_SequelAndImdbRating(movieName, sequel_new, rating){

    const movieObjects = await movieClass.collection.findOneAndUpdate(
        {movieName: movieName}, 
        { 
            $set: {
                sequel: sequel_new,
                imdbRating: rating
            }
        }
        );
    return movieObjects
}

// Function Call
let update_output_2 = update_SequelAndImdbRating('The Nun', true, 8.5);
update_output_2.then(function(response) {
    console.log(response, 'output response')
})

10. UPDATE (Asynchronous)

Sometimes we don’t need to wait for DB to update, so we can initiate the function and move on to the next line of code and the update can happen in due course of time.

const movieClass = require('./movieSchema')

// // UPDATE
// Function to Update Array
function update_MovieCast(movieName, newCast){

    movieClass.collection.findOneAndUpdate(
        {movieName: movieName}, 
        { 
            $push: {
                cast: `${newCast}`},
        }
        );
    console.log('DB Updated');
}

// Function Call
update_MovieCast('Tenet', 'Michael Caine') // Update new cast in the movie tenet

// Function to Update Boolean and Number
function update_SequelAndImdbRating(movieName, sequel_new, rating){

    movieClass.collection.findOneAndUpdate(
        {movieName: movieName}, 
        { 
            $set: {
                sequel: sequel_new,
                imdbRating: rating
            }
        }
        );
    console.log('DB Updated');
}

// Function Call
update_SequelAndImdbRating('The Nun', true, 8.5)

11. Delete (Synchronous)

Typically we can also delete the record by specifying a unique id based on the documents.

const movieClass = require('./movieSchema')

// // DELETE
// Function Declaration
async function deleteMovie(name){
    const movieObjects = await movieClass.deleteOne( {movieName: name});
    return movieObjects
}

// Function Call
let delete_output = deleteMovie('The Nun');
delete_output.then(function(response) {
    console.log(response, 'output response
})

12. Delete (Asynchronous)

const movieClass = require('./movieSchema')

// // DELETE
// Function Declaration
async function deleteMovie(name){
    const movieObjects = await movieClass.deleteOne( {movieName: name});
    console.log('Document Deleted', name)
}

// Function Call
deleteMovie('Tenet');

13. Resources & References

I hope I was able to shed some light on the CRUD operations on MongoDB. You can check out the GitHub repo that contains the entire code implementation and feel free to break it.

GitHub — https://github.com/bala-codes/Database-CRUD-Operations

MongoDB — https://www.mongodb.com/

Until then, keep expecting the best from us as always!

Related posts:

Leave a Reply

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