Tuesday, September 21, 2021

Building Simple Rest API for CRUD using NodeJS, Express and MongoDB with JWT Tokens Authentication

Before going to the main topic, we need to install basic stuff. If you already installed everything you can skip and move to the next steps.


Step 1: Install Node, NPM and MongoDB


#1. Node and NPM :


Check if you have already installed Node and NPM on your system using the below command.


node -v

npm -v


if you have these things you will get a version for both like below, Otherwise you need to install. 


C:\Users\ELCOT>node -v

v14.17.3


C:\Users\ELCOT>npm -v

6.14.13

Download and Install the Node from this link here. NPM automatically install with node package.

#2. MongoDB: 

Download and Install Mongo Using this link here.

Step 2: Install NPM dependency Packages

Once Node and NPM are ready, We need to install some dependency packages to run and connect mongodb using node. Before that I need to create and initiate the node project.

npm init

Once you initialize the project, you have the package.json file in the folder.  Now we have started to install dependency packages. 

npm install express --save

npm install body-parser --save

npm install jsonwebtoken --save

npm install mongoose --save 

npm install nodemon --save


Use of these packages :

Express : Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.

Body Parser : Node.js body parsing middleware.

Parse incoming request bodies in a middleware before your handlers, available under the req.body property.

Note As req.body's shape is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting. For example, req.body.foo.toString() may fail in multiple ways, for example the foo property may not be there or may not be a string, and toString may not be a function and instead a string or other user input.

Mongoose : Mongoose is a Node. js based Object Data Modeling (ODM) library for MongoDB. It is akin to an Object Relational Mapper (ORM) such as SQLAlchemy for traditional SQL databases. The problem that Mongoose aims to solve is allowing developers to enforce a specific schema at the application layer.

Nodemon : nodemon is a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected. 

Step 3 : Building Rest API for Simple CRUD Operation with JWT Tokens Authentication


Below code structure i'm following, If you need you can follow with your wish.



src/controllers/auth.js


const express = require('express')

const config = require('../../db.config');

const router = express.Router();

const User = require('../models/user.model');

const jwt = require('jsonwebtoken');

 

router.post('/', function (req, res) {

    User.findOne({ email: req.body.email }, function (err, user) {

        if (err) {

            return res.status(500).send('Error to connecting server.');

        }

        if (!user) {

            return res.status(404).send('User Not found.');

        }

        var isValidPassword = req.body.password == user.password ? true : false;

        if (!isValidPassword) {

            return res.status(401).send({ auth: false, token: null })

        };

        var token = jwt.sign({ id: user._id }, config.secret, {

            expiresIn: 3600

        });

        res.status(200).send({

            auth: true, token: token

        });

    });

});

 

module.exports = router;


src/controllers/user.js

const express = require('express')

const router = express.Router()

var jwt = require('jsonwebtoken');

var config = require('../../db.config');

const User = require('../models/user.model');

 

router.post('/', authenticationToken, function (req, res) {

    if (!req.body) {

        return res.status(400).send({

            message: "Please fill all required fields"

        });

    }

 

    const user = new User({

        firstName: req.body.firstName,

        lastName: req.body.lastName,

        email: req.body.email,

        phone: req.body.phone,

        userName: req.body.userName,

        password: req.body.password

    });

 

    user.save()

        .then(data => {

            res.send(data);

        }).catch(err => {

            res.status(500).send({

                message: err.message || "Getting error while creating new user."

            });

        });

});

 

router.get('/', authenticationToken, function (req, res) {

    User.find().then(users => {

        res.send(users);

    }).catch(err => {

        res.status(500).send({

            message: err.message || "Getting error while fetching users."

        });

    });

});

 

router.get('/:id', authenticationToken, function (req, res) {

    User.findById(req.params.id)

        .then(user => {

            if (!user) {

                return res.status(404).send({

                    message: "User not found with id " + req.params.id

                });

            }

            res.send(user);

        }).catch(err => {

            if (err.kind === 'ObjectId') {

                return res.status(404).send({

                    message: "User not found with id " + req.params.id

                });

            }

            return res.status(500).send({

                message: "Error getting user with id " + req.params.id

            });

        });

});

 

router.put('/:id', authenticationToken, function (req, res) {

    if (!req.body) {

        return res.status(400).send({

            message: "Please fill all required fields"

        });

    }

 

    User.findByIdAndUpdate(req.params.id, {

        firstName: req.body.firstName,

        lastName: req.body.lastName,

        email: req.body.email,

        phone: req.body.phone,

        userName: req.body.userName,

        password: req.body.password

    }, { new: true })

        .then(user => {

            if (!user) {

                return res.status(404).send({

                    message: "user not found with id " + req.params.id

                });

            }

            res.send(user);

        }).catch(err => {

            if (err.kind === 'ObjectId') {

                return res.status(404).send({

                    message: "user not found with id " + req.params.id

                });

            }

            return res.status(500).send({

                message: "Error updating user with id " + req.params.id

            });

        });

});

 

router.delete('/:id', authenticationToken, function (req, res) {

    User.findByIdAndRemove(req.params.id)

        .then(user => {

            if (!user) {

                return res.status(404).send({

                    message: "user not found with id " + req.params.id

                });

            }

            res.send({ message: "user deleted successfully!" });

        }).catch(err => {

            if (err.kind === 'ObjectId' || err.name === 'NotFound') {

                return res.status(404).send({

                    message: "user not found with id " + req.params.id

                });

            }

            return res.status(500).send({

                message: "Could not delete user with id " + req.params.id

            });

        });

});

 

function authenticationToken(req, res, next) {

    var token = req.headers['x-access-token'];

    if (!token) {

        return res.status(403).send({

            auth: false, message: 'Please Provide the token.'

        });

    }

    jwt.verify(token, config.secret, function (err, decoded) {

        if (err) {

            return res.status(500).send({

                auth: false, message: 'Failed to authenticate token.'

            });

        }

        req.userId = decoded.id;

        next();

    });

}

 

module.exports = router;



src/models/user.model.js


const mongoose = require('mongoose');

 

const UserSchema = mongoose.Schema({

    firstName: String,

    lastName: String,

    email: String,

    phone: String,

    userName: String,

    password: String

}, {

    timestamps: true

});

 

module.exports = mongoose.model('User', UserSchema);



src/db.config.js


module.exports = {

    secret: 'simpleCrud',

    url: 'mongodb://localhost:27017/simple-crud'

}



src/server.js


const express = require('express');

const bodyParser = require('body-parser');

 

const app = express();

const port = process.env.PORT || 4000;

 

app.use(bodyParser.urlencoded({ extended: true }))

app.use(bodyParser.json())

 

const dbConfig = require('./db.config');

const mongoose = require('mongoose');

mongoose.Promise = global.Promise;

mongoose.connect(dbConfig.url, {

    useNewUrlParser: true

}).then(() => {

    console.log("Connected Successfully");

}).catch(err => {

    console.log('Unable to connect the database.', err);

    process.exit();

});

 

const userController = require("./src/controllers/user")

app.use('/api/users', userController)

 

const authController = require('./src/controllers/auth')

app.use('/api/auth', authController)

 

app.listen(port, () => {

    console.log(`Node server is listening on port ${port}`);

});



Once every code changes ready, Please run mongodb using command


mongod


and then run node js using command 


node server.js


Now your API is ready. Node run in the default port 4000. You can call the API using any tool like postman. (http://localhost:4000/api/login , http://localhost:4000/api/user)


Note : We need to send the token on Header with param x-access-token for all API. You're getting a token on the Login API response. Right now the token expires in 1 hour. If you need, you update the expiration time on auth.js.


 If you face any issue or clarification, Please hit me with comments. Thanks!


1 comment: