In this tutorial, you will master the MERN Stack CRUD Operations by building a PhoneBook app.
Specifically, we will create a simple phone book app that allows a user to add a phone number along with a name, update the phone number, view the phone number list, and delete phone numbers.
We will use MongoDB to store our data, ExpressJS to handle the backend, and ReactJS to handle the front end.
By the end of this tutorial, you will be able to create your own CRUD application using the MERN stack and have a complete understanding of a full-stack project.
In this tutorial, we will divide our topics into two parts.
First, we will implement our backend portion with Express, and Mongoose, and then we will implement our frontend portion with React.
- Environment Setup for Backend
- Create Node Server
- Connect MongoDB Database
- Create PhoneBook model
- Create Post Route
- Create Get Route
- Create Update Route
- Create Delete Route
- Environment setup for Frontend
- Add data with Axios.post
- Get data with
Axios.get
- Update data with
Axios.put
- Delete data with
Axios.delete
Setup Backend for MERN Stack CRUD
Setting up the environment is the most important part of any application development because, without a proper environment, you can not implement any functionality.
For the backend, we need to install some important packages. But at first, we will create a folder named phonebook-app
and inside this folder, we will create two folders client
and server
.
The client folder will be used for implementing the frontend part and the server folder will be used for implementing the backend part. Let’s see the below image of our project folders:
This is what our project folders will look like. Now let’s implement the backend functionality inside the server folder to do so, first, we need to change our directory to the server folder
. For the first time, when you open the phonebook folder in your Vscode the directory will be set as phonebook, and to change it you need to run a command in your terminal as cd server
Step 1:
Now, we are ready to implement backend functionality in our server folder. At first, we have to give a command in our terminal and the command is
npm init
or
npm init -y
This command will create a package.json
file for us, from where we will be able to manage our installed packages and also control the version of our application.
If you want to create the package manually then you need to give the command npm init
and if you want to create the file as a whole then you need to type npm init -y
Step 2:
In this step, we will install our necessary packages like Express, and Mongoose. Express is a popular NodeJS framework and the Mongoose is the Object Data Modeling library for MongoDB.
We will run the NPM
command to install these. See the below example :
npm install express mongoose cors
or
npm i express mongoose cors
Here, with the help of this command, we will be able to install both at a single time.
The express will use to write NodeJS code and set up endpoints and Mongoose will be used to create a database model and save data.
The CORS is referred to as Cross-Origin-Resource-Sharing which will help us to build a connection with the front end.
After installing these, our package.json file will look like this:
Here, we have set the script as “start”: “node index.js”
and you can see those packages with the version. Our environment has been set up successfully and now we are ready to go to the next part.
Create Node Server
To create a Node server with express we need to require the express library first and then we need to define a Port
number and then create a server with the help of app.listen()
We will write this code in the index.js
which is our app’s entry point. file See the below code example:
const express = require('express')
const cors = require('cors')
const app = express()
app.use(express.json())
app.use(cors())
const PORT = 8080
app.listen(PORT, () => {
console.log(`Server is running on PORT ${PORT}...`)
})
Here, you can see that we have set the port as 8080
now if we run the command as npm start
we will be able to see the output as Server is running on PORT 8080...
in the console.
Like, express we need to require Mongoose for counting the database and we also need the connection credentials from the Atlas MongoDB. Let me assume that, you already know about these. See the below code example of the connecting database:
const mongoose = require('mongoose')
const DB = 'mongodb+srv://<YOUR USERNAME>:<YOUR PASSWORD>@cluster0.zozv5.mongodb.net/myFirstDatabase?retryWrites=true&w=majority'
mongoose.connect(DB, {
useNewUrlParser: true,
useUnifiedTopology: true,
}).then(() =>{
console.log('Database connected..')
})
Here, we have saved the credentials in a variable named DB
and we are hiding the credentials of username
and password
You may set your own credentials on that variable.
Finally, we have connected the database with the help of Mongoose by using mongoose.connect()
We pass the variable there and write some code for avoiding the warnings.
After connecting the MongoDB database, it is time to create our database schema model.
That will give the shape of the data and define how the data will be stored in the database.
We will create it into a separate folder and will name it Model
and inside the model folder, we will create a folder named PhoneBook.js
Inside this folder, we will create a simple database schema model.
See the below code example:
const mongoose = require('mongoose')
const PhoneBookSchema = new mongoose.Schema({
name : {
type : String,
required : true
},
phone : {
type : Number,
required : true
}
})
const PhoneBook = mongoose.model('PhoneBook',PhoneBookSchema)
module.exports = PhoneBook
Here, you can see that we have created a schema model where we will store two types of data.
One is the name
which type is String
and another one is the phone
which type is Number
We also set them as required: true
as a result, it will throw a Mongo error if any fields remain empty.
Finally, we have exported this schema model so that we can use it by importing it into another file.
Now, we are ready to create a post route where users will post particular data by hitting on a specific route and the data will be stored in the database.
See the below code example where we have implemented this functionality:
const PhoneBook = require('./model/PhoneBook')
app.post('/add-phone', async(req,res) => {
const phoneNumber = new PhoneBook(req.body)
try{
await phoneNumber.save()
res.status(201).json({
status: 'Success',
data : {
phoneNumber
}
})
}catch(err){
res.status(500).json({
status: 'Failed',
message : err
})
}
})
At first, we imported the PhoneBook model and then create the post route with the help of the app.post()
inside this, we have set the ‘/add-phone’
that means if anyone types http://localhost:8080/add-phone
then he will be able to add data and the data will be saved in the database.
As we haven’t implemented the frontend functionality we will check this with the help of the postman. Let’s check the output:
Here, in the postman, you can see that we have typed the particular route and also typed a dummy name and phone number, and clicked the send button.
As a result, it shows us a success message with a unique id and refers that our data has been saved in the database.
Now to Crosscheck, we will open our MongoDB compass and try to see if there are any data with the name “test” or not.
Here, in the MongoDB compass, you can see that the exact data has been saved. That means we have successfully been able to implement our POST
functionality.
We have already created the post route. That means users can save the data but how can we see those data. To see those data we need to implement the get routes.
Here, we can create a get route with the help of the app.get()
, and to query data, we will use Model.find()
method. See the below code example of this implementation:
app.get('/get-phone', async (req,res) => {
const phoneNumbers = await PhoneBook.find({})
try{
res.status(200).json({
status : 'Success',
data : {
phoneNumbers
}
})
}catch(err){
res.status(500).json({
status: 'Failed',
message : err
})
}
})
Here, you can see that we have implemented the get route and we are using async-await Let’s check the functionality again with the help of the Postman below:
Here, you can see that we are getting the data successfully from the database with the help of hitting the specific route.
Sometimes, we need to update the data and to perform this action we will implement the update route where we will be able to update the data by hitting the specific route.
We will use the findByIdAndUpdate()
method where we will be able to find a specific id and then change the value of that id’s data. See the below code example :
app.patch('/update-phone/:id', async (req,res) => {
const updatedPhone = await PhoneBook.findByIdAndUpdate(req.params.id,req.body,{
new : true,
runValidators : true
})
try{
res.status(200).json({
status : 'Success',
data : {
updatedPhone
}
})
}catch(err){
console.log(err)
}
})
Here, we have to select the id first and then update the value of this. Let’s check the postman in the below:
Here, you can see that we have updated the previous data with the name updated Test, and it shows a success message in the Postman.
Let’s check the database also if the data is actually updated or not.
You can see that the data has been successfully updated in the database also. In the next step, we will implement the delete route.
In our application, sometimes we need to delete a particular data from the database and we can do that by creating a delete route. We will create the functionality of deleting a single item from the database with the help of an id.
Because we do not want to delete the whole data from the database. Let’s see the below code of this:
app.delete('/delete-phone/:id', async(req,res) => {
await PhoneBook.findByIdAndDelete(req.params.id)
try{
res.status(204).json({
status : 'Success',
data : {}
})
}catch(err){
res.status(500).json({
status: 'Failed',
message : err
})
}
})
Here, at first, we search for specific data with the help of the findByIdAndDelete() method and simply set the value of that data empty. Let’s check it in the below:
Here, you can see that we simply hit the delete route and send a delete request and it should delete the item from the database. Let’s check our database and see if the data has been deleted or not.
You can see that, there are no elements that exist in our database. That means our program is working successfully.
We have implemented the CRUD operation in the backend because now we can create data, read data, update data, and delete data. But, we are half done with this tutorial and to perform this action we need to use Postman. This is not our goal. We want to visible our data and perform these actions from the frontend. Our API is ready and now we are ready to implement our next part of this tutorial. we will use React as frontend technology and try to build a complete CRUD application that is based on the MERN stack.
To work with the frontend, at first we need to set up the environment for it. Till now, we were in the server directory and wrote our backend code.
Now, we have to shift our directory first and change the directory, as before, we need to open our phonebook app and then run the command cd client
into the terminal.
It will take us to the client folder. After that, we need to write another command that will create a demo react app for us. See the below command:
npx create-react-app .
With the help of this command, we will be able to install react and its necessary packages in our client folder. Here, npx create-react-app
is the main command, and the “dot(.)”
represents that the app will be created in the present folder that is the client
.
It will take some time to install all the packages for you and it depends on the internet connection. Make sure that you have connected your computer to the internet.
After the installation process has been finished, it will create a few files for you but all those files will not be necessary to complete the tutorial.
So, you may simply remove them. We have already done that and the file structure will look something like the below image:
You will find some extra files in your src
folder but to complete this project these files will be enough. Now we are ready to work with react and implement the frontend functionality.
At first, we will implement the adding data from the frontend. To do so we will use a medium that will help us to send a post request from the frontend to the backend.
We will use Axios
and to work with it we need to install this package. The installation process is the same, all we need to do is to run npm i axios
command in the terminal and it will install this package for us. Now, see the below code example of implementing the adding data:
import React, { useState } from 'react';
import Axios from 'axios'
import './App.css';
function App() {
const [name, setName] = useState('')
const [phone, setPhone] = useState(0)
const addNewNumber = () => {
Axios.post('http://localhost:8080/add-phone',{name,phone})
}
}
return (
<div className="container">
<label htmlFor="">Name: </label>
<input type="text" onChange={(e) => {setName(e.target.value)}}/><br/><br/>
<label htmlFor="">Phone: </label>
<input type="number" onChange={(e) => {setPhone(e.target.value)}}/><br/><br/>
<button onClick={addNewNumber}>Add New Number</button>
</div>
);
}
export default App;
Here, at first, we have imported react, Axios, and CSS files. After that, inside the app function, we use react useState
which will let us hold the data.
Next, we define a function named addNewNumber
and define the route that we have already implemented in the backend and simply send the data from the frontend.
Later on, inside the return, we take a div and also take two input fields so that the user can input their name and phone number and finally, takes a button.
We implemented those functions inside these fields so that when the user hits the Add New Number button with all the necessary data it will save the data in the database.
Let’s try to test it in the below section.
To test it, you need to run the both app frontend and backend and you can simply run it by giving the npm start command in your terminal.
You can split the terminal like this in your VSCode and run the above command in the terminal and also make sure that you are in the exact directory. Now let’s try to see the output:
Here, we have set some data and sent a post request from the front end. As we haven’t yet added the functionality to see the data in the frontend so we will check it from the database.
Here, you can see that new data has been created in our database with the exact same name and phone number that we have typed in the input field.
That means our first functionality is working perfectly. In the next step, we will implement the functionality of getting these data from the database and making them visible in the frontend.
To see the data we need to use the Axios.get()
request and define the route that we have set in the backend. See the below code of this implementation:
import React, { useEffect} from 'react';
import Axios from 'axios'
import './App.css';
function App() {
const [phonebook, setPhonebook] = useState([])
useEffect(() => {
Axios.get('http://localhost:8080/get-phone').then(res => {
setPhonebook(res.data.data.phoneNumbers)
})
},[])
return (
<div className="container">
<h1>PhoneBook List</h1>
{
phonebook.map((val,key) => {
return <div key={key} className="phone" >
<h1>{val.name}</h1>
<h1>{val.phone}</h1>
</div>
})
}
</div>
);
}
export default App;
Here, we use the react useEffect and this function will return an array of elements. Remember, we have sent the JSON request inside an extra data object and that is why we have to use es.data.data. phoneNumbers to get the access.
Finally, we run a map method to make visible the data in the frontend. Now, let’s check it by hitting the http://localhost:3000/get-phone route in the below section:
Here, you can see that are able to see the exact data from the frontend and this data has been coming from the backend. In the next step, we will implement the update functionality.
We have already implemented the update functionality in the backend. Now we will implement it from the frontend. See the below code example of doing it :
import React, { useState } from 'react';
import Axios from 'axios'
import './App.css';
function App() {
const [newPhone, setNewPhone] = useState(0)
const updatePhone = (id) =>{
Axios.put('http://localhost:8080/update-phone',{id, newPhone})
}
return (
<div className="container">
{
phonebook.map((val,key) => {
return <div key={key} className="phone" >
<h1>{val.name}</h1>
<h1>{val.phone}</h1>
<input type="number" placeholder='update Phone...' onChange={(e) => {
setNewPhone(e.target.value)
}}/>
<button className="update-btn" onClick={() => {updatePhone(val._id)}}>Update</button>
</div>
})
}
</div>
);
}
export default App;
Here, inside the map function, we have taken an input field and a button named it to update so that the user can update the phone number by clicking the update button. Let’s check the output:
Here, you can see that our data has been updated with 10000000 instead of 123456789 Let’s take a look into our database and see if there’s any changes that occurred or not.
You can see that our data has been updated from the database also. In the next step, we will implement our last CRUD operation that is implementing the delete functionality.
To delete the data we will make the Axios.delete()
request and simply pass the id of the element that we want to delete. Let’s see the below code:
import React, { useState } from 'react';
import Axios from 'axios'
import './App.css';
function App() {
const [newPhone, setNewPhone] = useState(0)
const deletePhone = (id) => {
Axios.delete(`http://localhost:8080/delete-phone/${id}`)
}
return (
<div className="container">
<h1>PhoneBook List</h1>
{
phonebook.map((val,key) => {
return <div key={key} className="phone" >
<h1>{val.name}</h1>
<h1>{val.phone}</h1>
<input type="number" placeholder='update Phone...' onChange={(e) => {
setNewPhone(e.target.value)
}}/>
<button className='update-btn' onClick={() => {updatePhone(val._id)}}>Update</button>
<button className='delete-btn'onClick={() =>{deletePhone(val._id)}}>Delete</button>
</div>
})
}
</div>
);
}
export default App;
Here, we have simply added the delete button and passed the id when a user clicks on the delete button it will trigger the Axios.delete()
and delete the data. Let’s check the output:
Here, the moment we will click on the delete button the test data will be vanished not only from the front end but also from the Database. Let’s check the database to confirm it.
You can see that there is no data exists in our database. With this, we have successfully completed the CRUD operation in our application.
Conclusion
We have successfully completed our MERN stack CRUD operations. In this whole tutorial, we have covered, how you can perform a CRUD operation not only from the backend but also from the frontend.
If you have completed this tutorial, now you are ready to build your own CRUD application that will be based on the MERN stack.
But there are a lot more things to do and this tutorial will give a quick kickstart about how to work with APIs, how to handle backend, frontend, and database, and also how to make a connection in between them.
Now, your task is to practice each section of this tutorial carefully. For the first time, you can practice it by seeing it and after that try to do it alone without any help.
If you have any confusion, feel free to make a comment, I will try to solve your problem.
It’s just the beginning of your journey, there are a lot more things you can take from this tutorial. Let me tell you what will be the next step after completing this tutorial