Learning the basics of MongoDB by Writing a User Registration API

Written by itsknk | Published 2020/04/23
Tech Story Tags: mongodb | javascript | nodejs | express | api | backend | coding | beginners

TLDR Learning the basics of MongoDB has been one of the things in my checklist for the past 6 months but hasn’t gotten around actually learning it until now. In order to understand writing queries and all I figured it’ll be better to make something instead of just reading and watching some tutorials. So, I’ve decided to make a user registration API i.e, a simple registration form with only sign up and sign in functions as of now. As learning MongoDB was the main intention, I decided just to make the core app which is the backend functionality.via the TL;DR App

Learning MongoDB has been one of the things in my checklist for the past 6 months but hasn’t gotten around actually learning it until now. In order to understand writing queries and all I figured it’ll be better to make something instead of just reading and watching some tutorials. So, I’ve decided to make a user registration API i.e, a simple registration form kinda thing with only sign up and sign in functions as of now. As learning Mongo was the main intention, I’ve decided just to make the core app which is the backend functionality and no front end as it’s not necessary and I hate to do it.
First install the required modules which are mongoose, express, and bcrypt.
npm install express mongoose bcrypt 
Now make a folder to keep all the core files and create users.js and userSessions.js file where users.js contains the user schema and the later one holds the user sessions schema.
Now first write the users schema involving the following attributes:
  • Email
  • Password
  • Signup Date
The contents of the users.js will be as follows:
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const UserSchema = new mongoose.Schema({
  email: {
    type: String,
    default: ''
  },
  password: {
    type: String,
    default: ''
  },
  isDeleted: {
    type: Boolean,
    default: false
  },
  signUpDate: {
    type: Date,
    default: Date.now()
  }
});
UserSchema.methods.generateHash = function(password) {
  return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
UserSchema.methods.validPassword = function(password) {
  return bcrypt.compareSync(password, this.password);
};
module.exports = mongoose.model('User', UserSchema);
I guess the schema part is self-explanatory and as for the bcrypt part first, we’ve to create a generateHash method that’ll encrypt the password and then another method validPassword to compare the password encrypted before to the password entered. 
Now let’s fill the file userSessions.js in order for us to store the sessions every time the user gets logged in. The code will be as follows:
const mongoose = require('mongoose');

const userSessionSchema = new mongoose.Schema({
  userId: {
    type: String,
    default: ''
  },
  timestamp: {
    type: Date,
    default: Date.now()
  },
  isDeleted: {
    type: Boolean,
    default: false
  }
});

module.exports = mongoose.model('UserSession', userSessionSchema);
Now to the crucial part, Signup… sign up will be the first thing we do as it is difficult to log in if a user has not signed up. This will involve creating a new file, adding an endpoint and changing the frontend to submit email and password. This involves creating file signup.js and this file will be handling almost the entire functionality like signing up, verifying email address, saving the user data, sign in, verifying the sign in and log out.
First, the signup part where we prompt the user to enter email and password then we’ll verify if the email already exists or not and if it exists we’ll say that it does or else we’ve to save the new user details. So, the code will be as follows:
  app.post('/api/account/signup', (req, res, next) => {
    const { body } = req;
    const {
      password
    } = body;
    let {
      email
    } = body;

    if (!email) {
      return res.send({
        success: false,
        message: 'Error: Email cannot be blank.'
      });
    }
    if (!password) {
      return res.send({
        success: false,
        message: 'Error: Password cannot be blank.'
      });
    }
    email = email.toLowerCase();
    email = email.trim();

    //Verify email doesn't exist
    User.find({
      email: email
    }, (err, previousUsers) => {
      if (err) {
        return res.send({
          success: false,
          message: 'Error: Server error'
        });
      } else if (previousUsers.length > 0) {
        return res.send({
          success: false,
          message: 'Error: Account already exist.'
        });
      }
      // Save the new user
      const newUser = new User();
      newUser.email = email;
      newUser.password = newUser.generateHash(password);
      newUser.save((err, user) => {
        if (err) {
          return res.send({
            success: false,
            message: 'Error: Server error'
          });
        }
        return res.send({
          success: true,
          message: 'Signed up'
        });
      });
    });
  }); // end of sign up
Now onto the sign-in part again it’s divided into 3 parts, first, accept the data user has entered then verify whether the email already exists and if it does then save the user session or else pop a message that the user doesn't exist and needs to signup before. And the code for that will be:
app.post('/api/account/signin', (req, res, next) => {
    const { body } = req;
    const {
      password
    } = body;
    let {
      email
    } = body;

    if (!email) {
      return res.send({
        success: false,
        message: 'Error: Email cannot be blank.'
      });
    }
    if (!password) {
      return res.send({
        success: false,
        message: 'Error: Password cannot be blank.'
      });
    }
    email = email.toLowerCase();

    User.find({
      email: email
    }, (err, users) => {
      if (err) {
        return res.send({
          success: false,
          message: 'Error: Server error'
        });
      } if (users.length!=1) {
        return res.send({
          success: false,
          message: 'Error: Invalid.'
        });
      }
      const user  = users[0];
      if(!user.validPassword(password)){
        return res.send({
          success: false,
          message: 'Error: Account already exist.'
        });
      }

      //save userSession
      const userSession = new UserSession();
      userSession.userId = user._id;
      userSession.save((err, doc) => {
        if (err) {
          return res.send({
            success: false,
            message: 'Error: Server error'
          });
        }
        return res.send({
          success: true,
          message: 'Valid Signin.',
          token: doc._id
        });
      });
  });
  }); //end of sign in
Now the only thing left is to handle the logout and as we’ve seen that there’s a boolean attribute “isDeleted” which was set to false now we just need to change it to true as it clears the session. The code is as follows: 
Now let's put together the entire code…
const User = require('../../models/users');
const UserSession = require('../../models/usersessions');

module.exports = (app) => {
  /*
   * Sign up
   */
  app.post('/api/account/signup', (req, res, next) => {
    const { body } = req;
    const {
      password
    } = body;
    let {
      email
    } = body;

    if (!email) {
      return res.send({
        success: false,
        message: 'Error: Email cannot be blank.'
      });
    }
    if (!password) {
      return res.send({
        success: false,
        message: 'Error: Password cannot be blank.'
      });
    }
    email = email.toLowerCase();
    email = email.trim();

    //Verify email doesn't exist
    User.find({
      email: email
    }, (err, previousUsers) => {
      if (err) {
        return res.send({
          success: false,
          message: 'Error: Server error'
        });
      } else if (previousUsers.length > 0) {
        return res.send({
          success: false,
          message: 'Error: Account already exist.'
        });
      }
      // Save the new user
      const newUser = new User();
      newUser.email = email;
      newUser.password = newUser.generateHash(password);
      newUser.save((err, user) => {
        if (err) {
          return res.send({
            success: false,
            message: 'Error: Server error'
          });
        }
        return res.send({
          success: true,
          message: 'Signed up'
        });
      });
    });
  }); // end of sign up

  app.post('/api/account/signin', (req, res, next) => {
    const { body } = req;
    const {
      password
    } = body;
    let {
      email
    } = body;

    if (!email) {
      return res.send({
        success: false,
        message: 'Error: Email cannot be blank.'
      });
    }
    if (!password) {
      return res.send({
        success: false,
        message: 'Error: Password cannot be blank.'
      });
    }
    email = email.toLowerCase();

    User.find({
      email: email
    }, (err, users) => {
      if (err) {
        return res.send({
          success: false,
          message: 'Error: Server error'
        });
      } if (users.length!=1) {
        return res.send({
          success: false,
          message: 'Error: Invalid.'
        });
      }
      const user  = users[0];
      if(!user.validPassword(password)){
        return res.send({
          success: false,
          message: 'Error: Account already exist.'
        });
      }

      //save userSession
      const userSession = new UserSession();
      userSession.userId = user._id;
      userSession.save((err, doc) => {
        if (err) {
          return res.send({
            success: false,
            message: 'Error: Server error'
          });
        }
        return res.send({
          success: true,
          message: 'Valid Signin.',
          token: doc._id
        });
      });



    });
  }); //end of sign in

  app.get('/api/account/verify', (req, res, next) => {
    const {query} = req;
    const {token} = query;
    UserSession.find({
      _id: token,
      isDeleted: false
    }, (err, sessions) => {
        if(err){
          return res.send({
            success:false,
            message: 'Err: Server Error'
          })
        }
        if(sessions.length!=1){
          return res.send({
            success:false,
            message: 'Err: Server Error'
          })
        }
        else{
          return res.send({
            success:true,
            message: 'No Error'
          })
        }
    });
  }); //end of Verify

  app.get('/api/account/logout', (req, res, next) => {
    const {query} = req;
    const {token} = query;
    UserSession.findOneAndUpdate({
      _id: token,
      isDeleted: false
    }, {
      $set:{isDeleted:true}
    }, null, (err, sessions) => {
        if(err){
          console.log(err);
          return res.send({
            success:false,
            message: 'Err: Server Error'
          });
        }

        return res.send({
          success:true,
          message: 'No Error'
        });
    });
  });
};
Now just run the signing.js file and use postman to send the post and get requests and we can see the user details in Mongo Compass. 
I know this is the most basic use of MongoDB but still, it’s a start and I want to dive deep into more concepts so that I can implement stuff that involves handling user-profiles and all.

Written by itsknk | I think, I code, I write. Lather, rinse, repeat; not always in that order.
Published by HackerNoon on 2020/04/23