MERN stack TODO application [Backend]
Tags: webdev, node, beginners, javascript
Published: 2021-07-19 12:56:44

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](https://insomnia.rest/)

## 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.

```bash
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

```bash
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.

```bash
npm i -D nodemon
```

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

```json
// 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`

```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

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

```

The `package.json` file should look as follows

```json
{
  "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

{% gist https://gist.github.com/mritunjaysaha/c0d14f8bddff905e3fdddb95d8018343 %}

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

```bash
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](https://cloud.mongodb.com)

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

1. Log in to [mongoDB](https://cloud.mongodb.com)
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`

   ![Add connection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w49xfzzwlmuu2r1tmwc2.png)

9. Create a database user. You'll need the `username` and `password` for the `MongoDB URI`.
10. Click on the `Choose a connection method`
11. Click on `Connect your application`
12. Select the following driver and version

    ![Connect Cluster](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzdi5nff5kjvmkyhn4p6.png)

13. 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`

{% gist https://gist.github.com/mritunjaysaha/133d0825109012eda55c014a566bd537 %}

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`

{% gist https://gist.github.com/mritunjaysaha/0d8af33025f9a37193bdd3acd22db644 %}

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

{% gist https://gist.github.com/mritunjaysaha/7a7250eaf83a21f8f0c360947509b57d %}

#### 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

{% gist https://gist.github.com/mritunjaysaha/52426b34c34063ebc135d47374056725 %}

#### 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

{% gist https://gist.github.com/mritunjaysaha/beadb6bd2f14e40b74d1f8f8e0ac7ce0 %}

`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

{% gist https://gist.github.com/mritunjaysaha/9e4d1f3708134270a42b87ffe55bae76 %}

#### 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

{% gist https://gist.github.com/mritunjaysaha/adf881c6ff8e0420dc1ad5c2d069a3ca %}

### 3 Testing the end points using `Insomnia`

- Creating a todo

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

  ![Create TODO](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/36q84r20mj3mwoxcvrai.png)

- Reading the todo

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

  ![read-todo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7fc756g2bmgultvo6bl.png)

  You can check the changes in [mongoDB](https://cloud.mongodb.com/) 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.

  ```json
  {
    "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.

  ![update-todo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cddjegzeq51d1vsppx2z.png)

- Deleting a todo

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

  ![delete-todo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pmib05l9fa8q8ymrt1n9.png)

### 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.

{% gist https://gist.github.com/mritunjaysaha/55bdf6cdbc55aba7c1eba28e67cb01b0 %}




You can see the entire code of this blog in [GitHub](https://github.com/mritunjaysaha/todo/tree/master/server)