#4 How to Create, Request, Update, and Delete documents with Node.js and MongoDB?
Corey Domingos
2 years ago

In our previous how-to, we walked through how to setup your Rest API with Node.js and Express.js. Don't worry if you missed it! The project setup should only take a couple minutes and you can find it here. In todays how-to we will walk through how to create a connection to MongoDB — and perform CRUD (Create, Request, Update, and Delete) functions for a simple online store application. Now this how-to will require you to have Mongo installed on your machine, and so were not working in a terminal we will be installing a GUI for MongoDB being MongoDB Compass.


Prerequisites


Let's Build

One of the many perks of implementing MongoDB as your database store with Node.js, is being able to utilize the Mongoose library. Mongoose is a Object Data Modeling library that we can install into our project to streamline the data management process between our Node.js REST API, and our MongoDB collections. Sounds pretty cool right? Let's get started!

We will need to install the Mongoose library into our existing project, so navigate into your root directory and execute the following command:

npm i mongoose

If Mongoose successfully installed you'll be able to see it in your "package.json" file as shown below.

{
  "name": "myrestapi",
  "version": "1.0.0",
  "description": "my node api",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node app.js"
  },
  "author": "corey domingos",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.2",
    "mongoose": "^6.1.8"
  }
}

With Mongoose successfully installed, follow this guide to setup your local MongoDB instance and create a database using Compass if you wish to do so. Now with the local MongoDB instance setup, lets structure our project to handle the incoming requests we will be building out in this how-to. At your project root directory, create three directories being "controllers", "models", and "routes". In the "models" directory create a JavaScript file labeled "db.js". In the "db.js" file we will create our connection to our MongoDB instance to look like below:

Note: If you are building a production level application, your MongoDB URI should be stored in some sort of secure file storage live a ".env" or "config" file to avoid security breaches.

//require mongoose
var mongoose = require('mongoose'); 

//our mongo connection URI
var mongoDB_URI = 'mongodb://localhost:27017/RestApi'; 

//create our connection to the local MongoDB instance 
mongoose.connect(mongoDB_URI,
    (err) => {
        if (!err) {
            //alert if a successful connection is made 
            console.log('Successful MongoDB connection')
        }
        else {
            //alert if there is an error in the connection
            console.log("Error connecting to MongoDB: " + JSON.stringify(err, undefined, 2)); 
        }
    }
);

Modify the existing "app.js" file to require our new "db.js" file as below:

var express = require('express'); 
var app = express(); 
var port = 8080; 

//added to require the MongoDB config 
require('./models/db');

app.get('/testApi', (req, res) => {
    res.status(200).json({status: true, messsage : "The api is working!"})
})


app.listen(port, () => {
    console.log(`Server is listening on port: ${port}`)
})

Execute the start command for the app in your terminal:

node app.js

If all is well you should be getting two logs in your console, one for our existing server notification to tell us that the server is live, and one for our newly created MongoDB connection to our local instance.

Server is listening on port: 8080
Successful MongoDB connection


Mongoose Model

Now that we have a successful connection to our local MongoDB instance — lets define a mongoose model. In the "models" directory, create a new file named "basic.js" and modify like below:

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

//define our collection to write to our data base
var basicSchema = new mongoose.Schema({
    title: String, 
    price: Number,
    quantity: Number,
    productId: Number
})


//export the model
mongoose.model('Basic', basicSchema); 

With the model defined, we need to give the "db.js" file access to this file by adding the below to the "db.js" file:

require('./basic');


CRUD Functions

With the Basic model defined it's time to configure some Create, Request, Update, and Delete endpoints for the REST API to handle. In the "routes" directory create a file "basic.router.js" to handle our incoming Client Side requests (in this case it would be the REST client), and in the controllers directory create a file named "basic.controller.js" to handle the associated logic for each API endpoint. Now to read the incoming JSON request from the Client side we will need to install body-parser with the below command:

npm i body-parser

With body-parser installed, let's import mongoose into the controller and require the Basic model that we defined above.

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

//require the basic model
const Basic = mongoose.model('Basic'); 

Now in the same "basic.controllers.js" file, we'll add in the methods to handle the logic for creating a new item, requesting the items, updating an item, and deleting an item like below:

//Method to handle creating a new record 
module.exports.createItem = (req, res) => {

    //create an instance of our model
    var basic  = new Basic()

    //assign the fields defined in the model to the incomming request data
    basic.title = req.body.title; 
    basic.price = req.body.price; 
    basic.quantity = req.body.quantity; 
    basic.productId = req.body.productId; 

    //save the new document
    basic.save((err, obj) => {
        if (err) {
            //return err response if incountered
            res.status(422).json({status: false, error: "Error in creating item"}); 
        }
        else {
            //return success response
            res.status(200).json({status: true, message: "Item successfully created"}); 
        }
    })
}

//method to handle getting all items
module.exports.getItems = (req, res) => {
    //get all items in the collection
    Basic.find()
    .then((items) => {
        if (!items) {
            //no documents found return error
            res.status(404).json({status: false, error: "No items found"}); 
        }
        else {
            //return the items in the response
            res.status(200).json({ items }); 
        }
    })
}

//method to handle updating the quantity of a item
module.exports.updateItem = (req, res) => {
    //find the item to update
    Basic.findOneAndUpdate({productId : req.params.id}, 
        {$set : { quantity: req.body.quantity}}, 
        {new : true}, (err, updatedObj) => {
            if (err) {
                res.status(422).json({status : false, error : "Item not updated"}); 
            }
            else {
                res.status(200).json({ updatedObj }); 
            }
        })
}

//method to handle deleting an item
module.exports.deleteItem = (req, res) => {
    //find the item we want to delete
    Basic.findOneAndDelete({productId : req.params.id}, (err, deletedObj) => {
        if (err) {
            res.status(404).json({status: false, error: "Item not found"});
        }
        else {
            res.status(200).json({status: true, message: "Item successfully deleted"}); 
        }
    })
}

Now we can update the "basic.router.js" file in the "routes" directory to implement these methods:

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

//instance of express router
const router = express.Router(); 

//instance of the basic controller
const Basic = require('../controllers/basic.controller'); 

//define the CRUD routes 
router.post('/createItem', Basic.createItem); 
router.get('/getItems', Basic.getItems);
router.put('/updateItem/:id', Basic.updateItem); 
router.delete('/deleteItem/:id', Basic.deleteItem); 

//export router
module.exports = router; 

With this done, we just have to make some alterations to our "app.js" to make use of the new router files and the body-parser package we installed. The "app.js" should be modified like below:

var express = require('express'); 
var app = express(); 
var port = 8080; 
const bodyParser = require('body-parser');

require('./models/db');
const basicRouter = require('./routes/basic.router');

//instance of the express app & implement BodyParser
var app = express(); 
app.use(bodyParser.json());

//allow access to the router file
app.use('', basicRouter); 

app.get('/testApi', (req, res) => {
    res.status(200).json({status: true, messsage : "The api is working!"})
})

app.listen(port, () => {
    console.log(`Server is listening on port: ${port}`)
})

Recompile the server with the start command:

node app.js

With the Mongo Schema, route handling, and method logic built to handle our four REST API endpoints at:

  • localhost:8080/createItem
  • localhost:8080/getItems
  • localhost:8080/updateItem/:id
  • localhost:8080/deleteItem/:id

We can now test these endpoints with our REST Client tool to insure that they are working properly.


Endpoint Testing


Create request

In your REST Client, make a POST request to "localhost:8080/createItem". Now the fields that the createItem() method in the 'basic.controller.js" file is going to be expecting to come from this request are "title", "price", "quantity", and "productId" — so we need to make sure the REST Client is sending these fields in the body. Input the below into the body section of the POST request being sent by the REST Client:

{
    "title" : "shirt",
    "price" : "100",
    "quantity" : "10",
    "productId" : "1" 
}

After this POST request is made, you should be able to view this document created in the Basic Schema in MongoDB Compass — or terminal depending on your use case. Additionally we can check to see if the request was successful by checking the response in the REST Client. If successful you should see the below:

{
  "status": true,
  "message": "Item successfully created"
}

Get request

In your Rest Client, make a GET request to "localhost:8080/getItems". You should see the below in the REST Client response:

{
  "items": [
    {
      "_id": "61f35a22acfe3911ff83e5ed",
      "title": "shirt",
      "price": 100,
      "quantity": 10,
      "productId": 1,
      "__v": 0
    }
  ],
}

Update request

In your Rest Client, make a PUT request to "localhost:8080/updateItem/1", where "1" is the "productId" for the record that we are looking to update. Now the method updateItem() in the "basic.controller.js" file is going to be looking for us to pass it a new "quantity" value, so structure your PUT request body in your REST Client like below to update the quantity:

{
    "quantity" : "9"
}

After this PUT request is made, the returned document should reflect "9" for the quantity as below:

{
  "updatedObj": {
    "_id": "61f35a22acfe3911ff83e5ed",
    "title": "shirt",
    "price": 100,
    "quantity": 9,
    "productId": 1,
    "__v": 0
  }
}

Delete request

In your REST Client make a DELETE request to "localhost:8080/deleteItem/1", where "1" is the "productId" for the record that we are looking to delete. You should see the below in the REST Client response:

{
   "status": true,
   "message": "Item successfully deleted"
}


Conclusion

In just a couple easy steps we have updated our existing REST API to handle Create, Request, Update, and Delete endpoint calls to perform REST API functions on documents in MongoDB. Pretty cool right? The source code for this project can be found on my Github here.

Interested in creating a conversation on this blog post? Create a post in our Node.js community!

Written by
Corey Domingos
Specializing in Full-Stack modern web development, with a focus in Rest API development & deployment. Find my StackCafe community profile at coreydom!
Yes I would like to receive emails from the StackCafe Team for newsletters, how-to's, and community updates.