Sagar Medtiya
Sagar Medtiya

Sagar Medtiya

How to create simple RESTful API in node.jsπŸš€βœ…

How to create simple RESTful API in node.jsπŸš€βœ…

Sagar Medtiya's photo
Sagar Medtiya
Β·Nov 3, 2022Β·

6 min read

Subscribe to my newsletter and never miss my upcoming articles

Play this article

Table of contents

In this blog, we will learn how to build a simple API using ExpressJs and MongoDb with CRUD functions for Users and respective posts.

Introduction

API is an acronym for Application Programming Interface which has became an integral part of Software Development.

RESTful API's are API's that conform the REST architectural style. The acronym for REST is Representational State Transfer which is an architectural style that defines a set of properties and constraints based on HTTP.

We'd be requiring runtime environment and applications:

  • NodeJS
  • Insomnia
  • MongoDB compass
  • VScode

So, lets get started without further ado

Create Project File

// Create directory for your new project 
mkdir restAPI
// Navigate into the directory
cd restAPI

Initialize NodeJs project with yarn init.

Next, we'd be installing Express, Nodemon, CORS and Setup Server installation

// Installation of required packages
yarn add express cors bcrypt body-parser dotenv joi mongoose
// Installation of developer dependency
yarn add -D nodemon

It will take a while to install the dependencies considering your internet speed.

Let's write some code. For the IDE I'll go for vs code. Use your preferred IDE to open the project directory and create a file app.js and modify package.json as shown below.

{
  "name": "rest-api",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "dev": "nodemon server.js",
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcrypt": "^5.0.1",
    "dotenv": "^14.1.0",
    "body-parser": "^1.20.0"
    "express": "^4.17.2",
    "joi": "^17.6.0",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^6.2.9",
    "cors": "^2.8.5"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}

In this file I have made app.js as the entry file, now we will code the file and setup our server.

Coding

app.js file

Create the app.js file and add the code.

require('dotenv').config();
const express = require('express');
const app = express();
const mongoose = require('mongoose');
const cors = require('cors');
const bodyParser = require('body-parser');
const PORT = process.env.PORT || 3000;
app.use(cors())
app.use(bodyParser.json())

// Import Routes
const postsRoute = require('./routes/posts');
const userRoute = require('./routes/user');

app.use('/posts', postsRoute);
app.use('/user', userRoute);

//Routes
app.get('/', (req, res) => {
    res.send('We are on HomePage')
});

// Connect MongoDB
mongoose.connect( process.env.MONGODB,{ useNewUrlParser: true, useUnifiedTopology: true
    }).then(()=>{
        console.log('Connection Successful');
    }).catch((error)=>{     
        console.log('Something went wrong', error)
    });

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

Code Explanation

  • We are defining the variables and importing the packages.
  • mongoose.connect() is used to connected to the MongoDB Compass.
  • app.listen() is used to start local server at PORT:3000

    Now run the server by npm start and you'll get this.

Screenshot 2022-10-31 132834.png

Create Model Schema for Database

User Schema

./models/User.js

const mongoose = require('mongoose');

const UserSchema = mongoose.Schema({
    name: {
        type: String,
        required: true,
        min: 6,
        max: 255
    },
    email: {
        type: String,
        required: true,
        min: 6,
        max: 255
    },
    password: {
        type: String,
        required: true,
        min: 6,
        max: 1024
    },
    date: {
        type: Date,
        default: Date.now
    }
});

module.exports = mongoose.model('User', UserSchema);
Code Explanation
  • name: Username of the user.
  • email: It is used to verify the email using joi validation.
  • password: password for the user.
  • date: Automatically, assigned at the time of creation of post.

Post Schema

./models/Post.js

const mongoose = require('mongoose');

const PostSchema = mongoose.Schema({
    userID: {
        type: String
    },
    title: {
        type: String,
        required: true
    },
    description: {
        type: String,
        required: true
    },
    date: {
        type: Date,
        default: Date.now
    }
});
  • userID: The id of the user who created the post.
  • title: Title of the post.
  • description: Description for the post.
  • date: Automatically, assigned at the time of creation of post.

User Login and Registration Validation

Validation.js

const Joi = require("joi");

const registerValidation = data=>{
      const RegisterSchema = Joi.object({
            name: Joi.string().min(6).required(),
            email: Joi.string().min(6).required(),
            password: Joi.string().min(6).required()
      });
      return RegisterSchema.validate(data);
};

const loginValidation = data=>{
    const LoginSchema = Joi.object({
        email: Joi.string().min(6).required().email(),
        password: Joi.string().min(6).required()
    });

    return LoginSchema.validate(data);
}

module.exports.loginValidation = loginValidation;
module.exports.registerValidation = registerValidation;

Code Explanation

  • Joi allows us to create blueprints of Javascript objects that ensure that we process and ultimately accept accurate data.
  • After creating the object we are using the JoiObject.validate(data) function to validate the our accepted data.

Create Routes to access model and database

./routes/user.js

We will implement the following endpoints

  • POST /user/register
  • POST /user/login
  • DELETE /user/{uid}/only
  • DELETE /user/{uid}/all
const express = require("express");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken")
const router = express.Router();

//MongoDB Model
const User = require("../models/User")
const Post = require("../models/Post")

//validation import 
const { registerValidation, loginValidation } = require("../validation");

//register user
router.post("/register", async(req, res)=>{
    //validate user 
    const { error } = registerValidation(req.body);
    if(error){
        return res.status(200).send(error.details[0].message);
    }
    //check if User already in databse
    const emailExist = await User.findOne({email: req.body.email});
    if(emailExist){
        return res.status(400).send("Email already exists");
    }

    //Hash Password
    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(req.body.password, salt);

    //Validate and create user
    const user = new User({
        name: req.body.name,
        email: req.body.email,
        password: hashedPassword
    });

    try{
        const savedUser = await user.save();
        res.json({name: user.name, email:user.email});
    }
    catch(err){
        res.json({message:err});
    }
});

//Login User
router.post("/login", async(req,res)=>{
    const {error} = loginValidation(req.body);
    if(error){
        return res.status(400).send(error.details[0].message);
    }
    // check if email exists
    const user = await User.findOne({email:req.body.email});
    if(!user) return res.status(400).send("Email Does Not Exist");

    const validPass = await bcrypt.compare(req.body.password, user.password)
    if(!validPass ) return res.status(400).send("Invalid Password");

    //Create & assign token
    const token = jwt.sign({_id: user_id}, process.env.TOKEN_SECRET);
    res.header("auth-token",token).send(token);
});

//Delete user

router.delete('/:uid/only', async(req,res)=>{
    try{
        const removedUser = await User.remove({_id:req.params.uid});
        res.json(removedUser);
    }
    catch(err){
        res.json({message:err});
    }
});

router.delete('/:uid/all', async(req, res) => {
    try {
        const removedUser = await User.remove({ _id: req.params.uid, get: req.body });
        const removedPosts = await Post.deleteMany({ userID: req.params.uid });
        res.json(removedUser);
        res.json(removedPosts);
    } catch(err) {
        res.json({ message: err });
    }
});

module.exports = router;

Code Explanation

  • We are creating POST method for registration and login requests and validating the information/data.
  • If any error happens, we are returning the error and messages.
  • For the password we are generating 10 rounds of salt and encrypted with that. During login we are encrypting the login password and comparing with the hashed password in the database.
  • API to delete the user.

./routes/posts.js

We will implement the following endpoints:

  • POST /posts/{uid}create new post for the user
  • GET /posts/{uid} get all posts related to the user
  • GET /posts/{uid}/{postID} get specific post related to the user
  • UPDATE /posts/{uid}/{postID} update the post related to that user using uid for validation and postID to update post title
  • DELETE /posts/{uid}/{postID} delete all data regarding the posts about the user
const express = require("express");
const router = express.Router();

const Post = require("../models/Post")

//create a post for the user
router.post('/:uid', async(req,res)=>{
    const post = new Post({
        userId: req.params.uid,
        title: req.body.title,
        description: req.body.description
    })
    try{
        const savedPost = await post.save();
        res.json(savedPost);
    }
    catch(err){
        res.json{message:err};
    }
})

//Get all posts for user
router.get('/:uid', async(req,res)=>{
    try{
        const posts = await Post.find({ userID:{ $eq: req.params.uid }});
        console.log(posts)
        res.json(posts);
    }
    catch(err){
        res.json({message: err});

    }
})
// get specific posts of user
router.get("/:uid:/postId", async(req,res)=>{
    try{
        const post =await Post.findById(req.params.postId);
        if(post.userID === req.params.uid){
            res.json(post);
        }
        else{
            res.json({message: "Incorrect User"})
        }

    }
    catch(err){
        res.json({message:err});
    }
});

// update specific post of a user
router.update('/:uid/:postId', async(res,req)=>{
    try{
        const updatedPost = await Post.updateOne({ _id: req.params.postId, userID: {$eq: req.params.uid}},{
            $set:{
                title: req.body.title
            }
        });
        res.json(updatedPost);
    }catch(err){
        res.json({message:err});
    }
});

// Delete Specific post of a user
router.delete('/:uid/:postId', async(req, res) => {
    try {
        const removedPost = await Post.remove({ _id: req.params.postId, userID: { $eq: req.params.uid } });
        res.json(removedPost);
    } catch(err) {
        res.json({ message: err });
    }
});

module.exports = router;

Points to Remember

  • Don’t forget to replace your own MONGODB-KEY in .env file
  • Better to create TODO then execute it. For example, Screenshot 2022-11-03 134133.png

References

Did you find this article valuable?

Support Sagar Medtiya by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
Β 
Share this