Have you ever wondered how the front end and the back end work together to form a complete full-stack application? You probably have heard the term API(Application Programming Interface). It is a mechanism with a functional role that facilitates the communication between the front end and the back end in a web application. API is the interface that makes the communication between the software components like the server, database, and client possible.

In this in-depth article, you will learn how to build a CRUD API with Node.js and MongoDB using the Express framework.

What is a CRUD API?

CRUD is the abbreviation for Create, Read, Update and Delete. These are the four primary operations performed in databases.

  • Create(C): The operation adds new entries in the database table or collection.
  • Read(R): The operation reads the data from the database and returns it based on search parameters.
  • Update(U): The operation modifies the existing entry in the database.
  • Delete(D): The operation removes an entry from the database table or collection.

Therefore, CRUD API refers to the API that performs these various operations. It can create, read, update and delete records in the database using the following HTTP methods.

  • GET: It reads the data from the server.
  • POST: It creates new resources in the server.
  • PUT: It updates the data in the server. It takes parameters to identify the specific data item. It updates the whole content of the specific document.
  • DELETE: It deletes the data in the server. It also takes the parameter in the request to identify the item as in the PUT method.

How to Build a CRUD API with Node.js and MongoDB

The article will walk you through the steps of creating a CRUD API using the Node.js Express framework. At the end of the article, you will create a Task Management App that lets you create, view, modify and remove the tasks.

Create the Project

If you don’t have Node installed on your computer, you can download it from the Node.js website. The npm package manager comes with the installation of Node.js, which will be used to install the packages and dependencies while creating the CRUD API. You can view the npm version installed in your system with the following command.

$ npm -v
8.6.0

Now, you are ready to go. Let’s start the project by creating a directory task-management-app. Don’t hesitate to name your directory as your wish.

The first step in the project is to create the soul of your project, the package.json file. Ensure you are inside the project directory and type the following command in the terminal.

npm init -y

The above command creates a file named package.json that is responsible for running the scripts and installing the dependencies in your project. The y flag used in the command indicates to use the default values instead of asking the questions. The initial package.json file looks like this:

//package.json
{
  "name": "task-management-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Install the Packages and Tools

The next step is to install the dependencies to your project. As stated earlier, you will be using npm to install the dependencies. Use the following command to install the dependencies to your project.

npm i express mongoose dotenv
npm i -D nodemon
  • express: It is a Node.js framework for creating a web application’s server and back-end architecture.
  • mongoose: It is a Node.js library for modeling the Object Data for MongoDB. In simpler terms, mongoose enables the application to communicate with the MongoDB database.
  • dotenv: It is a package that loads the environment variables to the application from the .env file.
  • nodemon: It is a tool that enables the express server to restart automatically after changes have been detected in the code. Notice, the use of the -D flag while installing it. The flag indicates that nodemon is a development dependency, meaning it only works in the development environment.

After the installation of the dependencies, the package.json file looks like this:

//package.json
{
  "name": "task-management-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^16.0.1",
    "express": "^4.18.1",
    "mongoose": "^6.4.4"
  },
  "devDependencies": {
    "nodemon": "^2.0.19"
  }
}

To test the API endpoints, you need to install one more tool in your system, Postman.

Creating the Express Web Server

Create a file server.js in the root project directory. In server.js, require the express library, which is the backbone of your application.

Next, require the dotenv library and invoke the config() method. Consequently, it enables you to read the environment variables stored in the .env file with the process.env global variable. Then, create your Express application in an app variable as follows.

//server.js
const express = require('express')
const dotenv = require('dotenv').config()
const app = express()

Create a file .env in the project directory and write the following.

//.env
PORT=8000

Now, you can access the PORT environment variable and listen on that port as follows in server.js.

//server.js
const port = process.env.PORT
app.listen(port, () => console.log(`app running on port ${port}))

Here, the listen() method creates a web server, and your application is live on the local server. However, you need to add a little tweak to run your server with the nodemon tool. For that, add the following line in the scripts key in the package.json file.

//package.json
 "scripts": {
    "server": "nodemon server.js"
  },

The wait is over. You can run your application with the command npm run server. Now you don’t need to restart your server every time you make changes in your code asnodemon will take care of it for you.

As you will be working with JSON and urlencoded data while using Postman, you need some tool to parse these kinds of requests. The good news is Express has built-in middlewares for these purposes and just needs to include these lines in the server.js file.

//server.js
app.use(express.json())
app.use(express.urlencoded({extend:false}))

The final server.js file looks like this:

//server.js
const express = require('express')
const dotenv = require('dotenv').config()
const port = process.env.PORT

const app = express()
app.use(express.json())
app.use(express.urlencoded({extend:false}))

app.listen(port, ()=>console.log(`app running on port ${port}))

Creating Routes

Now that the Express server is up and running, it’s time to create the routes in your application. Routing allows you to handle the HTTP requests by assigning the requests to the specific handler called controllers. Since you are creating a CRUD application, the routes will handle the four HTTP methods stated earlier in the article.

First, create a file named taskRoutes.js and add the following line of codes to the file.

//taskRoutes.js
const express = require("express");
const router = express.Router();

const {getTasks, setTask, updateTask, deleteTask} = require('./taskControllers.js')

router.get('/', getTasks)
router.post('/', setTask)
router.put('/:id', updateTask)
router.delete('/:id', deleteTask) 

module.exports = router

The router object handles the different requests to your application. When the server receives a GET request the Express get() method passes the control to the getTasks controller. Likewise, the controllers setTask, updateTask, and deleteTask will handle the POST, PUT and DELETE requests, respectively.

While updating and deleting a data item, you need to specify the item in the route with some identification. Therefore, the /:id parameter is used in the put() and delete() methods.

Now that the creation of routes has been completed, it is necessary to export the router object. Also, add the following line to require the router in server.js.

//server.js
app.use('/tasks', require('./taskRoutes'))

The /tasks route is appended to the port (const port = process.env.PORT) of the web server. For instance, the URL of the GET route will be http://localhost:8000/tasks/.

Note: As of now, the routes won’t work as the controllers used in the taskRoutes.js have not yet been created. For now, require them from a file tasksController.js which we will create later on.

Connecting the Application to the Database

You need a database to store, retrieve and manage the data for your application. You will be using MongoDB for this purpose. You can still download MongoDB locally in your system. Since the article is based on MongoDB Atlas, I recommend using Atlas.

Creating a Cluster

Sign in to MongoDB Atlas, a cloud database service you will use for your application. Or, if you have not created an account yet, create the account.

Next, choose the shared Shared plan, which is free. Then, fill in the relevant information in the picture below and create the cluster.

Options for creating a cluster in MongoDB

Creating Database Account

Create a database user account with a username and password.

Form for creating a MongoDB database user account

Adding IP Address to Access List

After that, choose My Local Environment and add your IP address to the Access List. Then, click the button Finish and Close.

Form for adding IP Address to Access List

Creating a Collection

A collection in a NoSQL database like MongoDB is equivalent to the table in a relational database like SQL. After creating the cluster, navigate to the Collections tab and click Add My Own Data to create your collection. Fill in the information about your collection as follows.

Form for creating database and collection name

Connecting the Application to the Database

Navigate to the Overview tab and click on Connect. Next, click on the second option, Connect your application.

Options to choose a database connection method

Then, select the driver and its version as in the picture below. A connection string will appear. After copying the connection string, click the Close button.

Connecting the database to the application

Open the .env file in your project and paste the connection string, which is similar to the following

//.env
MONGO_URI=mongodb+srv://subodh:subodh789@mycluster.kdbit.mongodb.net/taskManagementApp?retryWrites=true&w=majority

Note: Do not forget to replace <password> with your database password, and make sure to add the database name in the URI. Here, the name of the database is taskManagementApp.

Setting the Database Connection

In the project directory, create a file db.js and use the connect() method of the mongoose library to establish a database connection to the application.

//db.js
const mongoose = require('mongoose')

const connectDB = async ()=>{
    try{
        const con = await mongoose.connect(process.env.MONGO_URI)

        console.log(`Database Connection successfully established: ${con.connection.host}`)
    } catch(error){
      console.log(error)  
    }
}

module.exports = connectDB

The connect() method takes the connection string as the parameter which is stored in the .env file in the MONGO_URI variable. The code snippet above shows the variable is accessed with the process.env global variable.

After importing the connectDB function, require it in the server.js file and invoke the function.

//server.js
const express = require('express')
const dotenv = require('dotenv').config()
const port = process.env.PORT

const connectDB = require('./db')

connectDB()

const app = express()

app.use(express.json())
app.use(express.urlencoded({extend:false}))

app.use('/tasks', require('./taskRoutes'))

app.listen(port, ()=>console.log(`app running on port ${port}`))

Next, restart the server as the .env file has been changed. The output in the terminal looks like this.

> task-management-app@1.0.0 server
> nodemon server.js

[nodemon] 2.0.19
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node server.js`
app running on port 8000
Database Connection successfully established: mycluster-shard-00-01.kdbit.mongodb.net

It shows that the application is connected with the MongoDB database.

Creating Model

The model is the interface between the MongoDB database and the application’s business logic. You can define the data you want to store by creating a model. Additionally, the model is responsible for establishing the relationship between different objects in your application. For now, you will create a model for tasks.

Create a file taskModel.js in the root directory of the project. Inside the file, create a schemataskSchema as follows.

//taskModel.js
const mongoose = require('mongoose');

const taskSchema = mongoose.Schema(
    { 
        task:{
            type:String,
            required:[true, 'Please add a task'],
        }
    }, {
        timestamps: true
    }
)

module.exports = mongoose.model('Task', taskSchema)

Here, taskSchema defines the construct and content of your document. There are two contents in the schema, which are task and timestamps. At the end of the file, the schema is imported as Task using the mongoose model property.

Creating Controllers

The controller acts as an interface between the data and the presentation layers. In other words, the controller receives the data from the model, performs operations, and passes it to the presentation model. It queries data from the model and sends the data as a response in the JSON format.

Earlier, you required a controller taskControllers.js while creating taskRoutes.js and used four controllers in the routes:getTasks, setTask, updateTask and deleteTask. Now, you will create the exact file and create the function controllers.

First, create the file taskControllers.js in the root directory.

Then, you need to require the taskModel to fetch the data from the database.

//taskController.js

const Task = require('./taskModel')

Now it’s time to create the controllers for the CRUD operations.

CREATE

The mongoose create() function allows you to add new items to the database.

//taskControllers.js
const setTask = (req, res)=>{
    Task.create({
        task: req.body.task,
    }).then(task=>{
        res.status(200).json(task)
    })
    .catch(err=>{
        res.json(err)
    })
}

In the above code snippet, req.body.task will request the form data from the user. You can supply the data asapplication/x-www-form-urlencoded content type from Postman.

After creating a new data item, the document is returned as a response. If the creation fails, an error is returned.

READ

The mongoose find() function returns specific data from the database according to the query.

//taskControllers.js
const getTasks = (req, res)=>{
    Task.find()
    .then(tasks=>{
        res.json(tasks)
    })
    .catch(err=>{
        res.json(err)
    })
}

Since the find() function has no arguments in the code above, it returns all the data items from the database. In case of failure, an error is returned.

UPDATE

The findByIdAndUpdate() method searches the particular data item and replaces it with the new value.

//tasksController.js
const updateTask = (req, res)=>{
    Task.findByIdAndUpdate(req.params.id, req.body, {
        new:true,
    })
    .then(updatedTask=>{
        res.json(updatedTask)
    })
    .catch(err=>{
        res.json(err)
    })
}

The above code finds the task with the id supplied as a parameter in the request. Then, the new task obtained from req.body replaces the old task. If everything goes right, it returns the updated task. Otherwise, an error will be sent as a response.

DELETE

The findByIdAndDelete() function finds the record through its id and deletes it from the database.

//tasksController.js
const deleteTask = (req, res)=>{
    Task.findByIdAndDelete(req.params.id)
    .then(task=>{
        res.json(task)
    })
    .catch(err=>{
        res.json(err)
    })
}

Here, the task with the specific id is deleted, and the document is returned as a response. An error is returned as a response if it fails to delete the document.

After creating the controllers, do not forget to export the controllers at the end of the tasksController.js.

//taskController.js
module.exports = { setTask, getTasks, updateTask, deleteTask}

Sending Request to API Endpoints

Now that the CRUD API has been created, it is time to test the API by sending HTTP requests to the endpoints. You will be using Postman for testing the API. The table below shows the API endpoints.

MethodsURLsDescriptions
GET/tasks/Display all the tasks
POST/tasks/Create a new task
PUT/tasks/idUpdate the existing task
DELETE/tasks/idDelete the existing task

Sending POST Request

In Postman, open a new tab, enter http://localhost:8000/tasks/ in the URL bar and select POST from the drop-down menu. Next, click on the Body option right below the URL bar. Then, chose the x-www-form-urlencoded and write task for the key and my first task for value options. After that, click on the Send button.

The first POST request

You just sent a POST request to the server with some payload. After the request is received, a new document is created in the database and sent as a response. Again, send another POST request as follows:

The second POST request

Notice that task has the value my third task.

Sending GET Request

You have created two documents and can send the GET request to the server to read those documents from the database. Write the URL http://localhost:8000/tasks/ and send the GET request.

Sending GET request

In response, both documents are returned from the database.

Sending PUT Request

Remember, you created a task that says my third task. Suppose that it was a mistake. With a PUT request, you can change it to my second task. For that, grab the id of my third task and write the URL as http://localhost:8000/tasks/<id> where <id> is the id of the task. In my case, the url is: http://localhost:8000/tasks/62d4355f2cd70e918280d498.

Choose PUT from the drop-down menu. Next, write the value to be updated, as shown in the picture below.

Sending PUT request

The document is updated in the database, and the response returns the updated document.

Sending DELETE Request

The URL structure of the DELETE request is similar to the PUT request. It should have the id of the document which is to be deleted.

Sending DELETE request

As a response, the deleted document is returned. However, the data is deleted from the database.

Conclusion

Congratulations! You created a task management CRUD API on top of the Express framework using Node.js and MongoDB. This article demonstrated how to set up an Express server, manage the routes, create the models and controllers, and use a persistent database MongoDB to build a CRUD API. By this point, I hope you are able to create a CRUD API from scratch and implement it in your projects. 

Was this article helpful in that regard?

Let us know your thoughts with a reply on our Twitter.

Author