Building A CRUD REST API With MongoDB, Mongoose, And NodeJS



#Prerequisite:


Before we start make sure you have development environment.

  1. NPM 6.9.0

  2. NodeJS 10.16.3

  3. MongoDB 4.2.0

  4. IDE (Atom or Visual Studio)

To bootstrap our NodeJS project, we need to verify that we have NodeJS, NPM (node package manager) and MongoDB installed on our machine. To do that, open your terminal or command prompt and run

Verify NPM version

npm -v

Verify NodeJS version

node -v

Verify MongoDB version

mongo --version

After setting up and configuring the development environment, follow the steps to build the application

#Step 1: Initialize NodeJS project


To initialize the NodeJS project, first create a directory with the project name. Open command prompt / terminal and type following commands.

mkdir nodejs-mongodb-rest

Go to the project directory and initialize the NodeJS project

cd nodejs-mongodb-rest

Initialize NodeJs project with npm init follow the wizard to setup the project.

npm init

Following is the initialization wizard output

Bhupeshs-MacBook-Pro:nodejs-mongodb-rest bhupeshsinghpadiyar$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (nodejs-mongodb-rest) 
version: (1.0.0) 
description: CRUD REST Application with NodeJS & MongoDB
entry point: (index.js) 
test command: 
git repository: 
keywords: RESTFUL,API,NodeJS,json
author: Bhupesh Singh Padiyar
license: (ISC) 
About to write to /Users/bhupeshsinghpadiyar/Documents/GitHubPersonal/nodejs-mongodb-rest/package.json:

{
  "name": "nodejs-mongodb-rest",
  "version": "1.0.0",
  "description": "CRUD REST Application with NodeJS & MongoDB",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "RESTFUL",
    "API",
    "NodeJS",
    "json"
  ],
  "author": "Bhupesh Singh Padiyar",
  "license": "ISC"
}


Is this OK? (yes) yes
Bhupeshs-MacBook-Pro:nodejs-mongodb-rest bhupeshsinghpadiyar$ 

At this point, you should verify that you have a package.json file is available n your project root.

#Step 2: Configuring NodeJS with Express framework


We need to run a web server in order to make our API endpoint accessible to the browser, client application or a client tool like PostMan, we shall be using ExpressJS to achieve this.

Install ExpressJS with the following command

npm install express --save

#Step 3: Import the project to IDE


Use your preferred IDE to open the project directory and create a file index.js

This index.js file is entry point fo our application. Add the following code to index.js file.

index.js

// Import express
let express = require('express')
// Initialize the app
let app = express();
// Setup server port
var port = process.env.PORT || 8888;
// Send message for default URL
app.get('/', (req, res) => res.send('Welcome to NodeJS, Express Application'));
// Launch app to listen to specified port
app.listen(port, function () {
     console.log("Running NodeJS, Express application on port " + port);
});

#Step 4: Run the Node application


Use the following command to run the application from terminal / command prompt

node index.js

Following is the output of the command

Bhupeshs-MacBook-Pro:nodejs-mongodb-rest bhupeshsinghpadiyar$ node index
Running NodeJS, Express application on port 8888

Head to http://localhost:8080on your browser and you should see the output as following

#Step 5: Create routing, model and controller


Now our basic NodeJS application framework is ready. We will divide our NodeJS application in following three modules to maintain the modularity .

  1. Entity/Model - Mongo collection and entity mapping
  2. Routing - Defining REST API endpoints routes
  3. Controller - Controller to handle request and responses

To achieve this we will create three files in our project

  1. userModel.js
  2. api-routing.js
  3. userController.js

#Step 6: Mongoose installation, configuration & model mapping


In order to connect with Mongo DB we need to add MongoDB libraries.

Install mongoose:


Use following npm command to install.

npm install mongoose --save

Configure mongoose:


Once installation completed, we need to configure it in the application. Go to the index.js file, import mongoose & connect by providing the DB URL. Add the following piece of code to your index.js file.

let mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/users_rest', { useNewUrlParser: true, useUnifiedTopology: true});
var db = mongoose.connection;

// Added check for DB connection
if(!db)
    console.log("Error connecting db")
else
    console.log("Db connected successfully")

Here localhost:27017 is your database host and users_rest is the database name.

Mapping between MongoDB collection and the Model:


Once mongo configuration completed successfully, we need to map the MongoDB Collection and the model file in our code. Following are the user information that we have to save in MongoDB user collection,

  • id
  • firstName
  • lastName
  • email
  • age
  • address

Go to userModel.js file that we created previously and add the mapping as follows. The updated userModel.js looks as follows.

var mongoose = require('mongoose');
// Setup schema
var userSchema = mongoose.Schema({
    id: {
        type: String
    },
    firstName: {
        type: String,
        required: true
    },
    lastName: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    },
    age: {
        type: Number,
        required: true
    },
    address: {
        type: String,
        required: true
    },
    createdOn: {
        type: Date,
        default: Date.now
    },
    updatedOn: {
        type: Date,
        default: Date.now
    }
});

// Export User model

var User = module.exports = mongoose.model('user', userSchema);
module.exports.get = function (callback, limit) {
    User.find(callback).limit(limit);
}

Here for each field we need to define the type of the field and some other optional parameters like required, default etc. For more info about the other mapping attributes , follow the mongoose official documentation.

The MongoDB and model mapping part have completed here.

#Step 7: Add routing for the controller.


MondoDB configuration and mapping part is done. Now we will add the controller layer to serve different kind of request i.e. GET, POST, PUT, DELETE

Inside userController.js file we will perform 5 basic CRUD operations.

  1. List all the users
  2. Get specific user by id
  3. Create user
  4. Update user
  5. Delete user

Following is the final userController.js file after adding all five CRUD operations. In case of success response we will be setting HTTP status code as 200 & In case of any error from mongoose, we will be sending the HTTP status code as 500 with the error message thrown

// Import contact model
User = require('./userModel');
// Handle index actions
exports.index = async function (req, res){

    try {
        const response = await User.find({});
        res.status(200).json({
            status: 200,
            message: "Users retrieved successfully!!",
            data: response == null ? [] : response
        });
    } catch(err) {
        res.status(500).json({
            status: 500,
            message: err
        });
    }
};

// Handle create user actions
exports.new = async function (req, res) {

    // Create User model Object
    var user = new User();
    user.firstName = req.body.firstName;
    user.lastName = req.body.lastName;
    user.email = req.body.email;
    user.age = req.body.age;
    user.address = req.body.address;

    try {
         // save the user and check for errors
        const response = await user.save();
        res.status(200).json({
            status: 200,
            message: "New User created successfully!!",
            data: response
        });
    } catch (err) {
        res.status(500).json({
            status: 500,
            message: err
        });
    }
};

// Handle view contact info
exports.view = async function (req, res) {

    try {
        // save the user and check for errors
       const response = await User.findById(req.params.id);
       res.status(200).json({
           status: 200,
           message: "User details fetched successfully!!",
           data: response == null ? {} : response
       });
   } catch (err) {
       res.status(500).json({
           status: 500,
           message: err
       });
   }
};

// Handle update user info
exports.update = async function (req, res) {

    try {
        // save the user and check for errors
        const user = await User.findById(req.params.id);

        user.firstName = req.body.firstName;
        user.lastName = req.body.lastName;
        user.email = req.body.email;
        user.age = req.body.age;
        user.address = req.body.address;
        const saeUserResp = await user.save(req.params.id);
        res.status(200).json({
            status: 200,
            message: "User Info updated successfully!!",
            data: saeUserResp
        });
    } catch (err) {
        res.status(500).json({
            status: 500,
            message: err
        });
    }
};

// Handle delete user
exports.delete = async function (req, res) {

    try {
        // save the user and check for errors
        const deleteResponse = await User.remove({_id: req.params.id});
 
        res.status(200).json({
            status: 200,
            message: "User Info updated successfully!!",
            data: deleteResponse
        });
    } catch (err) {
        res.status(500).json({
            status: 500,
            message: err
        });
    }
};

Here we have five different methods to perform CRUD operation.

  • The first method index will be returning list of all the users from DB.
  • The second method new will be creating the user in the DB
  • The third method view will be returning details of specific user from the DB.
  • The fourth method update will update details of specific user in the DB.
  • The fifth method delete will perform delete operation to delete specific user’s data from the DB.

#Step 8: Add API routing for the controller.


Now we have to add the api routing in the api-routing.js file. This routing will decide for which controller and which method inside the controller we need to call based on the request method and api URL.

To achieve this add the following line inside your index.js file.

// Use Api routes in the App
app.use('/api', apiRoutes)

The above line means all the request coming from the /api URL will be redirected to the api-routes.js file. And inside this file we will be writing the redirection rules for the api based on their methods (GET, POST, PUT, DELETE).

Refer the following code in api-roures.js file

// Initialize express router
let router = require('express').Router();

// Set default API response
router.get('/', function (req, res) {
    res.json({
        status: '200',
        message: 'Welcome to NodeJS, Express & Mongoose Application!',
    });
});


// Import contact controller
var userController = require('./userController');
// Contact routes
router.route('/users')
    .get(userController.index)
    .post(userController.new);

router.route('/users/:id')
    .get(userController.view)
    .put(userController.update)
    .delete(userController.delete);

// Export API routes
module.exports = router;

Here is the quick explanation of the code above in api-routes.js file.

  • The default URL (/api) with GET type request will return simple JSON message (Welcome to NodeJS, Express & Mongoose Application!) will status code 200.
  • HTTP Requests coming from the /api/users route with GET type method will perform get all the users list operation in the controller layer.
  • HTTP Requests coming from the /api/users route with POST type method will perform create user operation in the controller layer.
  • HTTP Requests coming from the /api/users/:id route with GET type method will perform get user by id operation in the controller layer. Here :id is the id of the specific user passed in request URL
  • HTTP Requests coming from the /api/users/:id route with PUT type method will perform UPDATE operation for specific user in the controller layer. Here :id is the id of the specific user passed in request URL
  • HTTP Requests coming from the /api/users/:id route with DELETE type method will perform DELETE operation for specific user in the controller layer. Here :id is the id of the specific user passed in request URL

#Step 9: Verify code in index.js file


After MongoDB configuration and ROUTING configuration, the final index.js file code is as follows.

// Import express
let express = require('express')
// Initialize the app
let app = express();

// Import Body parser
let bodyParser = require('body-parser');
// Import Mongoose
let mongoose = require('mongoose');
// Configure bodyparser to handle post requests
app.use(bodyParser.urlencoded({
   extended: true
}));
app.use(bodyParser.json());
// Connect to Mongoose and set connection variable
// Deprecated: mongoose.connect('mongodb://localhost/resthub');
mongoose.connect('mongodb://localhost:27017/users_rest', { useNewUrlParser: true, useUnifiedTopology: true});
var db = mongoose.connection;

// Added check for DB connection
if(!db)
    console.log("Error connecting db")
else
    console.log("Db connected successfully")


// Import routes
let apiRoutes = require("./api-routes")

// Setup server port
var port = process.env.PORT || 8888;

// Use Api routes in the App
app.use('/api', apiRoutes)

// Send message for default URL
app.get('/', (req, res) => res.send('Welcome to NodeJS, Express Application'));
// Launch app to listen to specified port
app.listen(port, function () {
     console.log("Running NodeJS, Express application on port " + port);
});

#Step 10: Start the application and test the API endpoints.


Now we need to start the application and test the api endpoints

Start Application:

Start the application by the following command in the project root directory:

node index.js

Test API Endpoints:

In order to test the API endpoints you may use any REST client tools like POSTMAN, RESTCLIENT etc.

Following are the API endpoint, API method, API request body and response etc.

  1. Create user API

    API Endpoint:

    `http://localhost:8888/api/users`

    Request Method : POST

    Request Body:

    {
        "firstName": "Bhupesh",
        "lastName": "Singh",
        "email": "bhupeshpadiyar.com@gmail.com",
        "age": 32,
        "address": "Mont Kiara, Kuala Lumpur, Malasysia, 50480"
    }
    

    Response Body:

    {
        "status": 200,
        "message": "New User created successfully!!",
        "data": {
            "_id": "5f8eccc5fa26160abafa9e47",
            "createdOn": "2020-10-20T11:40:53.171Z",
            "updatedOn": "2020-10-20T11:40:53.172Z",
            "firstName": "Bhupesh",
            "lastName": "Singh",
            "email": "bhupeshpadiyar.com@gmail.com",
            "age": 32,
            "address": "Mont Kiara, Kuala Lumpur, Malasysia, 50480",
            "__v": 0
        }
    }
    
  2. Get all users data API

    API Endpoint:

    `http://localhost:8888/api/users`

    Request Method : GET

    Response Body:

    {
        "status": 200,
        "message": "Users retrieved successfully!!",
        "data": [
            {
                "_id": "5f8eccc5fa26160abafa9e47",
                "createdOn": "2020-10-20T11:40:53.171Z",
                "updatedOn": "2020-10-20T11:40:53.172Z",
                "firstName": "Bhupesh",
                "lastName": "Singh",
                "email": "bhupeshpadiyar.com@gmail.com",
                "age": 32,
                "address": "Mont Kiara, Kuala Lumpur, Malasysia, 50480",
                "__v": 0
            },
            {
                "_id": "5f8ecdf5fa26160abafa9e48",
                "createdOn": "2020-10-20T11:45:57.184Z",
                "updatedOn": "2020-10-20T11:45:57.184Z",
                "firstName": "Johny",
                "lastName": "Sins",
                "email": "johny.sins@gmail.com",
                "age": 41,
                "address": "Los Angeles, California, USA",
                "__v": 0
            }
        ]
    }
    
  3. Get individual user detail by ID

    API Endpoint:

    `http://localhost:8888/api/users/5f8eccc5fa26160abafa9e47`

    Request Method : GET

    Response Body:

    {
        "status": 200,
        "message": "User details fetched successfully!!",
        "data": {
            "_id": "5f8eccc5fa26160abafa9e47",
            "createdOn": "2020-10-20T11:40:53.171Z",
            "updatedOn": "2020-10-20T11:40:53.172Z",
            "firstName": "Bhupesh",
            "lastName": "Singh",
            "email": "bhupeshpadiyar.com@gmail.com",
            "age": 32,
            "address": "Mont Kiara, Kuala Lumpur, Malasysia, 50480",
            "__v": 0
        }
    }
    
  4. Update individual user detail by ID

    API Endpoint:

    `http://localhost:8888/api/users/5f8eccc5fa26160abafa9e47`

    Request Method : PUT

    1. Request Body:

      {
          "firstName": "Bhupesh",
          "lastName": "Singh PADIYAR",
          "email": "xyz@gmail.com",
          "age": 32,
          "address": "Mont Kiara, Kuala Lumpur, Malasysia, 50480"
      }
      

    Response Body:

    {
        "status": 200,
        "message": "User Info updated successfully!!",
        "data": {
            "_id": "5f8eccc5fa26160abafa9e47",
            "createdOn": "2020-10-20T11:40:53.171Z",
            "updatedOn": "2020-10-20T11:40:53.172Z",
            "firstName": "Bhupesh",
            "lastName": "Singh PADIYAR",
            "email": "xyz@gmail.com",
            "age": 32,
            "address": "Mont Kiara, Kuala Lumpur, Malasysia, 50480",
            "__v": 0
        }
    }
    
  5. Delete individual user by ID

    API Endpoint:

    `http://localhost:8888/api/users/5f8eccc5fa26160abafa9e47`

Request Method : DELETE

Response Body:

{
    "status": 200,
    "message": "User Info updated successfully!!",
    "data": {
        "n": 1,
        "ok": 1,
        "deletedCount": 1
    }
}

#Source Code:


https://github.com/bhupeshpadiyar/nodejs-mongodb-rest



Thank You. Happy Learning!!!