MERN stack TODO application [Backend]

We'll be creating a minimal full-stack app using the MERN stack (MongoDB for database, Express and Node for backend, and React for frontend) to perform CRUD operations.

Our app will allow users to

  • Create a todo
  • Read todos
  • Update a todo
  • Delete a todo

This series should enable you understand CRUD operations using MERN stack.

In this part (part1), we will

  • Initialize our backend using npm and install necessary packages
  • Set up a MongoDB database
  • Set up server using Node and Express
  • Create a database schema to define a Todo
  • Set up API routes to create, read, update and delete documents from the database
  • Testing our API routes using Insomnia

Before we get started

Prerequisites

One should have at least some basic understanding of fundamental programming concepts and some experience with HTML, CSS, JavaScript.

This post is not meant to explain the MERN stack, but it is a good introduction to building a full-stack app with it.

Install

  • VS Code or any other editor
  • Latest version of Node.js
  • Insomnia or Postman
  • Prettier VS code extension to format the code

Part 1: Creating Backend

1. Initializing our project

Create a new folder and name it anything that you like and then open the folder in VS code and run the following code from the command prompt.

npm init -y

After running this command you will find a package.json if the folder.

2. Setting up package.json

i. Install the following dependencies

Run the following commands in the terminal to install the dependencies

npm i cors dotenv express mongoose

cors: allows cross-origin api calls
dotenv: needed to access data from .env files
express: web application framework for node.js
mongoose: It is needed to define the database schema and connecting to mongoDB

ii. Install the following development dependencies

Now install the following development dependencies, -D is used to install the development dependencies.

npm i -D nodemon

After installing the dependencies the package.json folder should look as follows.

// package.json
{
  "name": "mern-todo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "config": "^3.3.6",
    "cors": "^2.8.5",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "mongoose": "^5.13.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.11"
  }
}

iii. change the main entry point to server.js

Now, create a server.js file and a .env. The server.js file will be the entry point of the server and the .env file will contain the MONGO_URI. We also have to make the following changes in the package.json

//package.json
{
  "name": "mern-todo",
  "version": "1.0.0",
  "description": "",
  "main": "server.js", //changed
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "config": "^3.3.6",
    "cors": "^2.8.5",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "mongoose": "^5.13.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.11"
  }
}

Now, create the following folders

  • config: Inside the config folder, create a file named db.js. This file will contain the required code for connecting to the MongoDB database.

  • controllers: The controllers folder will contain the files which will have the methods for the end points to communicate with the database.

  • models: The models folder, will contain the files which will define the MongoDB schema

  • routers: The routers folder will contain the files with the endpoints.

At this stage the file structure should look as follows

.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js
├── package-lock.json
└── package.json

iv. Change the scripts to the following

"scripts": {
  "start":"node server.js",
  "dev":"nodemon server.js"
}

The package.json file should look as follows

{
  "name": "mern-todo",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js", //added
    "dev": "nodemon server.js" //added
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "config": "^3.3.6",
    "cors": "^2.8.5",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "mongoose": "^5.13.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.11"
  }
}

v. Setting up server

We will do the following to setup the server

  • Import express
  • Initialize our app using express()
  • Set up a get method for the endpoint http://localhost:8000 using app.get()
  • Set the PORT to 8000 for our server to run
  • Have our app to listen to PORT using app.listen()
.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js <-- we are here
├── package-lock.json
└── package.json

The code will look as follows

And start the server using nodemon using the following code. Make sure you are running the following command from the project directory.

npm run dev

If the server has started successfully then it should show the following message in the terminal

[nodemon] 2.0.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node server.js`
server is running on http://localhost:8000

You can also open http://localhost:8000 on your browser.

vi. Getting the MONGO URI from mongoDB

To connect to the database we will need the link for the mongoDB collection.

  1. Log in to mongoDB
  2. Create a new project
  3. Build a cluster
  4. Select cloud provider
  5. Create cluster
  6. wait for the cluster to be created.
  7. Click on connect
  8. click on allow access from anywhere. Then Add IP address
  1. Create a database user. You'll need the username and password for the MongoDB URI.
  2. Click on the Choose a connection method
  3. Click on Connect your application
  4. Select the following driver and version

    Connect Cluster

  5. Copy the mongodb+srv and paste it in the .env file

vii. Setting up .env file

//.env
MONGO_URI = mongodb+srv://<username>:<password>@cluster0.owmij.mongodb.net

Replace the <username> and <password> with your database username and password which you will set in step 9.

viii. Connecting to database

.
├── config
│   └── db.js <-- we are here
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js
├── package-lock.json
└── package.json

Now, open the db.js file which is in the config folder and add the following changes.

  • Import mongoose
  • Import MONGO_URI from .env
  • Define the connectDB methof for connecting to the database
  • Export the connectDB method to be called in server.js

Add the following changes in the server.js file.

  • Import dotenv
  • Import connectDB method from config/db.js
  • Call the connectDB method.

Let us make the the following changes in server.js

Save the changes it will restart the server or use the command npm run dev. The terminal should show a message of MongoDB is connected which we have added in the db.js under the try block.

ix. Defining database schema

Create a todo.js file in the models folder. We will define the database schema in this file.

.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js <-- we are here
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js
├── package-lock.json
└── package.json
  • Import mongoose
  • Create a Schema called TodoSchema
  • We will add two fields for our todo; title and description
  • Type of title will be String and it is a mandatory field
  • Type of description will be String and it is not a mandatory field
  • Export the model

The code will look as follows

x. Defining the end points

.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js <-- we are here
├── .env
├── server.js
├── package-lock.json
└── package.json

We will define the end points for the CRUD operations

  • Import express
  • Initialize router
  • We will later import the methods for the endpoint from controllers
  • Define a GET method to read all the todo
  • Define a POST method to create a new todo
  • Define a PUT method to update a existing todo
  • Define a DELETE method to delete a existing todo
  • Export the router

The code will look as follows

xi. Defining the methods for the end points

We will define the methods for the end points in the controllers folder

.
├── config
│   └── db.js
├── controllers
│   └── todo.js <-- we are here
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js
├── package-lock.json
└── package.json
  • Import Todo model from models/todo
  • Define the following four methods
    • getAllTodo
    • postCreateTodo
    • putUpdateTodo
    • deleteTodo
  • Export all the methods

getAllTodo: The find() method will return all the todo in the collection. If the collection is empty then it will return a 404 error.

postCreateTodo: The create() method will create a todo and return a success message. Otherwise, it will return a 400 error.

putUpdateTodo: The findByIdAndUpdate() will require two parameters the id and data of the todo to be updated. The id parameter will be extracted from req.params.id.

deleteTodo: The findByIdAndRemove() method will require only one parameter that is the id of the todo.

xii. Adding the methods to the end points

.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js <-- we are here
├── .env
├── server.js
├── package-lock.json
└── package.json
  • Import the methods for CRUD operations
  • Adding the methods to the end points

xiii. Adding the routes end points in the server.js

.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js <-- we are here
├── package-lock.json
└── package.json

The final part of completing the backend is to add the endpoints to the server.js file.

  • Import routes/todo.js
  • Add the routes endpoints to the middleware

3 Testing the end points using Insomnia

  • Creating a todo

We will send a POST request to http://localhost:8000/api/todo

  • Reading the todo

We will send a GET request to http://localhost:8000/api/todo

You can check the changes in mongoDB in collections

  • Updating a todo

To update a todo we will send a PUT request to http://localhost:8000/api/todo/id

The id has to be taken from the response message of the server.

{
    "message": "Todo added successfully",
    "data": {
      "_id": "60ec0f9655f9735a60a2d967",
      "title": "test todo",
      "description": "test todo",
      "__v": 0
    }
  }

For updating the todo we will need the id. We will get the id from the _id from the preview tab. We can get the id from the preview after using the GET request and POST request.

  • Deleting a todo

To delete a todo we will send a DELETE request to http://localhost:8000/api/todo/id

4. Adding cors

.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js <-- we are here
├── package-lock.json
└── package.json

Added cors so that we can make the api calls from the frontend application like react.

You can see the entire code of this blog in GitHub

24