Building A Simple CRUD App With Node JS, Express JS, and MongoDB

Building A Simple CRUD App With Node JS, Express JS, and MongoDB

Currently, most of the websites operate on an API-based backend structure, where we just send a request from the front end of the website by calling an API and obtain the required results. In this article, we are going to build a simple CRUD (Create, Read, Update and Delete) app with the application of Node JS, Express JS, and MongoDB from the basics. Before we jump into the application, let’s look into the tools we are going to use.

MongoDB: It is one of the most used NoSQL databases. It does not require any particular schema and is a document-type database i.e, it stores data in a key-value format. It can be installed from here according to the required version. With the installation of MongoDB, we need to also install Mongo Compass. Mongo Compass is a tool that helps us to visualize the MongoDB database. For this application, we can install it on our private systems as a single cluster and host it on localhost to interact with our application or we can also use Mongo Atlas. Mongo Atlas is the cloud database service for MongoDB. It is free for certain resources. We can create a free database there and access it from our application. To set up MongoDB Atlas, follow the below steps:

  1. If you have not yet signed up to Mongo Atlas. Create a new account.
  2. Click on: Create Cluster -> Go for the free one for this demo project -> Click on Create-> Keep the default specifications -> Click on create cluster.
  3. Now we need to add the user for accessing the database. It is similar to creating a profile to access a resource. Go to Database Access -> Set Username and Password-> Finally Add the User
  4. We also need to add the IP, we will use to access the database. Go to Network Access-> Go to add IP -> Add the current IP
  5. We will have to wait until the MongoDB database cloud is deployed.
  6. Once deployed, we need to go to clusters -> then click on connect -> Click on connect to an application-> We then specify the application-> We need to copy the URL.
  7. In the URL, we need to replace the username and password and pass it as a constant string.

Node JS: Node JS is an open-source server environment that uses Javascript in the background. Node JS uses an event-driven asynchronous non-blocking programming approach. Now, what does that mean? Node JS actually runs on a single thread. So, technically it should be able to serve one request at a time. So, when there are multiple requests in that scenario, the requests will queue up and the system becomes slow. The requests made to Node JS are mostly IO-based. Here, Node JS’s non-blocking IO theory comes into play. When the main Node JS thread receives an IO request it just transfers the request to say, some workers which mostly are Kernel Level threads of the server system, and get backs to take another user request, instead of getting blocked with only 1 request. This is why Node JS is said to have a non-blocking IO. Now, you might wonder, then how Node JS handles the response of the requests. This is where the asynchronous nature of the programming approach comes in. Every IO request in Node JS has a callback function, which activates when the response comes back. Then Node JS thread again picks up the response in an asynchronous manner. This is why Node JS is said to be asynchronous. Node Js packages are installed and managed by NPM or Node Packet Manager. Node JS can be installed from here. It can also be installed using:

sudo apt update
sudo apt install nodejs
sudo apt install npm

We can verify the versions of the installed services using:

nodejs -v
npm -v

Express JS: Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. It acts as middleware and supports HTTP requests. Express JS also helps to create the routing architecture for the application.

So, now as we have seen all the required elements, let’s move on to the application.

To build an app we first need to initiate the node packet manager in the folder using:

npm init

Once we initiate the packet manager, it creates a package.json file with the following required fields and the author field.

Next, we need to install the required dependencies for Node JS using npm install <package_name>.

For a simple CRUD app, we need some specific libraries. They are:

  1. Express: As we discussed, Express JS is required, so we need to install it. Installation: npm install express
  2. Nodemon: This library helps to update the server automatically whenever we make some changes to the source code. Installation: npm install nodemon.
  3. Mongoose: It helps in establishing connections and query MongoDB. Installation: npm install mongoose.

Once the library dependencies are installed, we need to go to the package.json again and change the “scripts” field to

"start": "nodemon index.js"

The reason to change this is, actually our full application is launched from the “index.js” file. Now, every time we make a change in the source code, we want Nodemon to start the application script in development mode.

Systems like Node JS are currently operated on an MVC (Model View Controller) architecture. It’s a design pattern. The idea is that it helps to focus on a specific part of the application and build it on a modular basis. The components are:

Model: It represents the structure and constraints of the data stored in the database.

View: It is the way the required data is presented to the user as per the need of the user.

Controller: This section controls the requests of the user and generates the appropriate response which is fed to the user.

Now, after going through the basic structure, we will start creating the required files and folders.

So, we first create a file “index.js”, which is our launching script page.

In the “index.js” file we include express and mongoose, and initialize the express app:

const express= require('express');
const mongoose= require('mongoose');

const app=express();

The “require” keyword is used to import modules into our program code. This is the old format though. In the new versions, we can use the “import” keyword.

Next, we need to establish a database connection with our MongoDB server.

const url= "mongodb://localhost:27017";

mongoose.connect(url,{useNewUrlParser: true});
const con= mongoose.connection;
app.use(express.json());
try{
con.on('open',() => {
console.log('connected');
})
}catch(error)
{
console.log("Error: "+error);
}

The above script will help us to connect to MongoDB. If we use Mongo Atlas, we just need to replace the variable “url” with the URL obtained from the Atlas as shown above. The statement app.use() lets the application use certain features. Here the element “express.json” helps the Node application to parse JSON data. We can also use the body-parser library for this purpose.

Now, we are ready to host the application.

const port=9000;
app.listen(port, () =>{
console.log('Server started');
})

We just need our application to listen to a particular port. We chose port 9000 here.

Once we have hosted the application successfully, let’s take a look at how to define the structures of the data we are going to use for the application. In this application, we are simply going to create a student record application.

We create a folder named “Model”. The folder holds all the model structures of data stored in the database for the project. We are going to use NoSQL, but still, we need a basic structure that will state, which fields should be there and the constraints on the fields. So, inside the Model folder, we create a “studentdata.js” file. The file is supposed to contain the structure of the student data entry in the database. Now, for every component, we will need a model file. As we have a single component, only the student data, we have only one file in the “Model” folder. So, let’s see how to define the model.

To build a model, we need to use Schema and model functions from the Mongoose library. The mongoose.schema() function helps to define the schema of the database and the mongoose.model() function helps to convert the schema and export it as the data model.

const mongoose =require('mongoose');

const studentSchema = mongoose.Schema({
name: {
type: String,
required: true,
},
roll: {
type: String,
required: true,
unique: true,
},
registration: {
type: String,
required: true,
unique: true,
},
subjects: {
type: [String],
required: true,
},
registered_on: {
type: Date,
default: new Date(),
},

})

var studentdata=mongoose.model('studentdata',studentSchema);
module.exports= studentdata;

The above code shows, how to create a schema and the data model. For every field, we must define the features as shown. The “subjects” field is an array and so represented as an array of strings. Again, for every field, we must define the type of the variable. After declaring, we need to export the model, so that we can use the functionalities in the other parts of the backend. So, we export the model as the variable “studentdata”. Here we have used “module.exports” command, but it later ES6 versions “export” keyword works.

Next, we go for the two most important parts: Routes and the Controllers. Routing is the part where the APIs are actually created and hosted. Normally we do not need to create the controllers but it’s a good practice as sometimes if there are too many control functions, it becomes very hard to manage if we stack them all the routing files. So, we define the functions in the Controllers part and import them in the routing section to keep the operation handling smoother.

So, let’s see what routing actually is. When say a user wants to query a data from the database or wants to push data into the database, similarly delete or update, the frontend issues requests in the form of API calls. Now, there are different requests for each issue. For querying, we have GET request, for sending data we have POST requests. These are called HTTP requests. They enable interactions between client and the server and work as a request-response protocol. The HTTP requests are:

GET is used to request data from a specified resource.

POST is used to send data to a server to create/update a resource.

HEAD: Same as GET, but it transfers the status line and the header section only.

PUT: Replaces all the current representations of the target resource with the uploaded content.

DELETE: Removes all the current representations of the target resource given by URI.

CONNECT: Establishes a tunnel to the server identified by a given URI.

PATCH: The PATCH method applies partial modifications to a resource

Now, to use these requests express js has routing function.Route definition takes the following structure:

app.METHOD(PATH, HANDLER)

Where:

  • app is an instance of express.
  • METHOD is an HTTP request method, in lowercase.
  • PATH is a path on the server. (URL path)
  • HANDLER is the function executed when the route is matched. (Handler function)

The method is the required request, if it is a query the method will be GET method and so on. The PATH is the request URL. Say, we request on

www.abc.com/xyz

then “/xyz” is the path. The handler function is the asynchronous callback function executed when the request is done. So, lets see how the routers are actually created.

First, we create a “Routes” folder in which all the routing files are created. For each subpart of domain, we create a seperate file. For example:

“xyz”, “pqr”, “cde” all are subdomains for the abc.com domain. For each of the routes we create seperate js files in the Routes folder. Ours is a very simple app, with only “/students” path, so we have only one js file in our Routes folder.

const express = require("express");

const student_Act = require("../controllers/students");

const router = express.Router();

router.get('/', student_Act.getStudents);
router.get('/:roll', student_Act.getspecStudent);
router.post('/', student_Act.createstudent);
router.patch('/:roll', student_Act.updatestudent);
router.delete('/:roll', student_Act.deletestudent);

module.exports=router;

The above code shows how to create a router. Now, you can see we have used “/” as path not “/students”. This is because here we will use the extension “students” in the path when we embed the whole thing in our index.js file. We have imported all the functions from our controllers folder, which we will create next. The handler functions have been passed accordingly with every route. Now, in some places, we see the path to be “/:roll”, this is way of parameter passing. In this project, we have used roll field as the primary key to identify each student. So, it has been passed as parameters in some routes as the primary key to identify the student. The parameters have been passed when we need to obtain the data for a specific student, update or delete a specific student data.

We can pass the parameters as:

  1. Using a parameter string in the form of the parameter string Example: www.abc.com/student/23 where 23 is the parameter, we need to frame the URL in the backend as:
    app.get(‘/path/:id’)
  2. Using the query string: We can pass the parameters as a query string of the format:
    Example: app.get(‘path’)

Once we create the router we export it to embed it in the index js file.

Now, let’s go to the controller part and define our functions to be used in the Routes portion.

To start we create a folder named “Contollers”. In which we define js files correspondingly with the Routes folder structure. In this case, we create the file “students.js” correspondingly as in the “Routes” folder.

In the js file, we first import the dependencies, the model we created, the express router, and mongoose. Next, we define the functions as follows:

Querying all students data

const getStudents = async (req, res) => {
try {
const student= await Student.find();

res.status(200).json(student);
} catch(error) {
res.status(404).json({message: error.message});
}
}

The above function is used to query all the student data from the database. We can do this using the .find() function from mongoose. We have used “async” and “await” keywords as the database query takes time and so the asynchronous property of node js comes in. Now, we have passed to parameters “request” and “response”. These two parameters are passed for every HTTP request from client to server.

Querying all students data

const getspecStudent = async (req,res) => {
const roll = req.params.roll;

try {
const stud = await Student.findOne({roll: roll});

res.status(200).json(stud);
} catch(error) {
res.status(404).json({ message: error.message});
}
}

As we can see, in the second statement, we have retrieve the roll parameter from the URL. We can use findone() to find the specific student with a roll as the query field.

Posting students data into database

const createstudent = async (req, res) => {
console.log(req.body);
const newstudent = new Student({
name:req.body.name,
roll:req.body.roll,
registration:req.body.registration,
subjects:req.body.subjects,
created_on:req.body.created_on

})
try {
await newstudent.save();

res.status(201).json(newstudent);

} catch(error) {
res.status(400).json({ message : error.message});
}

For the post request here, we have obtained the request body as shown.

Similarly, we have also created the update and delete functions. After we create all the functions, we export them to be imported and used in our Routing files.

Once every thing is created, we go back to our “index.js” file and embed the routing there, as shown.

const studentrouter= require("./routes/students");
app.use('/students',studentrouter)

So, every route in the student.js file can be accessed at the “/students” route.

Once everything is in place, we can kick start our server using:

npm start

We can visualize the results using apps like The Postman.

The above image shows how we can create a POST query using Postman on the http://localhost:9000/students URL. The body is passed as Raw JSON.

This is how we can create a simple CRUD app.

The project link can be found here.

Hope this helps!!!.