How To Make Registration And Login form using Node.js and MongoDB

In this article, we will take a look at how to implement a registration form and login form using Node.js and MongoDB. The reason I am using Node.js is the fact that Node.js is the latest and most advanced platform for the web front-end which have a power to run simultaneously and can use the power of a server to the full extent.

As we already know Node.js is used for creating a server-side application. So for building a registration form and login form, we need to build a server-side application.

First, we have to create the project. Open the terminal and write the below code, and press enter.

npm init

Login form using Node.js and MongoDB

Once you enter the command, you must fill in some information about your backend project. Once it has been done, we will follow the next step.

🔹First Step

We will make a folder structure for our project that includes folders like controller, DB, middleware, modules, routes, validator, and one file index.js in the src folder. You can see the picture below.

Login form using Node.js and MongoDB

After creating this folder structure, we must make a .envfile at the root level. We store our database URL in that file so we can access it anywhere in the project using the process. env.<URL_NAME>

Before starting coding, we have to install some npm packages, and they are listed below.

Let me give you some idea about these libraries.

npm install dotenv

dotenv is used to read the key and value pair from .env and add them to an environment variable. It is great for managing app settings during development.

npm install express

Install the Express framework globally using NPM so that it can be used to create a web application using the node terminal.

npm install bcrypt

Install this NPM package in our project to convert the user’s password into a hash.

npm install express-validator

The express-validator is used for checking users’ requests where we can check recommendations like length, min-max character, empty or not, and so on.

npm install http-status-codes

This package is used for the status code, which we return with a response.

npm install jsonwebtoken

This package generates a unique web token from a user’s data and the verified tokens when we pass with the APIs.

npm install cors mongoose shortid

These are other packages that we need to make this project complete.

So after installing all the packages successfully, we are moving forward to the code section, where we will see how to make the APIs using these packages.

🔹Second Step

In the DB folder, make one file, give the name connect.js, and write the code below. The purpose of this code is only to connect our backend to the database.

const mongoose = require("mongoose");
const connectDB = (url) => {
  return mongoose.connect(url);
};
module.exports = connectDB;

Now In our index.server.js, write below the code for connection.

const express = require("express");
require("dotenv").config();
const connectDB = require("./db/connect");
const app = express();
var cors = require("cors");
const authRouter = require("./routes/auth");
app.use(cors());
app.use(express.json());
app.use("/api", authRouter);
//Port and Connect to DB
const port = process.env.PORT || 5000;
const start = async () => {
  try {
    await connectDB(process.env.MONGO_URL);
    app.listen(port, () => {
         console.log(`Server is running on port ${port}`);
    });
  } catch (error) {
      console.log("error =>", error);
  }
};
start();

In this file, we wrote a code to establish the connection between the backend and the database. We called the start method for reference, and we used some packages for .env configuration, route configuration, and all those things.

One more change in the package.json file as well. We will use nodemon, so we don’t need to run our backend repeatedly. Without nodemon, we need to run our backend after making some changes to the code. Otherwise, it will not reflect. So nodemon is good to use.

"scripts": {
   "start": "nodemon src/index.server.js"
},

🔹Third Step

In the third step, we will write a User schema in the modal folder where we create boundaries for the APIs, like which data will allow and key names, etc.

const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const userSchema = new mongoose.Schema({
  firstName: {
     type: String,
     require: true,
     trim: true,
     min: 3,
     max: 20,
  },
  lastName: {
     type: String,
     require: true,
     trim: true,
     min: 3,
     max: 20,
  },
  username: {
     type: String,
     require: true,
     trim: true,
     unique: true,
     lowercase: true,
     index: true,
  },
  email: {
     type: String,
     require: true,
     trim: true,
     unique: true,
     lowercase: true,
  },
  hash_password: {
     type: String,
     require: true,
  },
  role: {
     type: String,
     enum: ["user", "admin"],
     default: "user",
  },
  contactNumber: {
     type: String,
  },
  profilePicture: {
     type: String,
  },
},{ timestamps: true });
//For get fullName from when we get data from database
userSchema.virtual("fullName").get(function () {
  return `${this.firstName} ${this.lastName}`;
});
userSchema.method({
  async authenticate(password) {
     return bcrypt.compare(password, this.hash_password);
  },
});
module.exports = mongoose.model("User", userSchema);

Like this, we define the keys for requests and can make a virtual method using mongoose. Here we made one method for authentication. We can compare passwords with the database and return the result from here.

By using virtual, we can directly make one key-value pair and give it to the DB response.

🔹Fourth Step

In the third step, we will make a file auth.js in the validator folder. After that, we are going to write a code for request validation.

const { check, validationResult } = require("express-validator");
const { StatusCodes } = require("http-status-codes");


const validateSignUpRequest = [
check("firstName").notEmpty().withMessage("First Name is required"),
check("lastName").notEmpty().withMessage("Last Name is required"),
check("email").isEmail().withMessage("Valid Email required"),
check("password")
   .isLength({ min: 6 })
   .withMessage("Password must be at least 6 character long"),
];
const validateSignIpRequest = [
check("email").isEmail().withMessage("Valid Email required"),
check("password")
    .isLength({ min: 6 })
    .withMessage("Password must be at least 6 character long"),
]
const isRequestValidated = (req, res, next) => {
  const errors = validationResult(req);
 
  if (errors.array().length > 0) {
      return res
              .status(StatusCodes.BAD_REQUEST)
              .json({ error: errors.array()[0].msg });
  }
  next();
};
module.exports = {
  validateSignUpRequest,
  isRequestValidated,
  validateSignIpRequest,
};

We wrote three validation here for good sign-in/signup requests. You can see we used the express-validator package to handle validation. This validator is used to check whether the requested data is enough or right to succeed in an API call.

🔹Fifth Step

Make an auth.js file into the router folder and write the below code

const express = require("express");
const router = express.Router();
const { signUp, signIn } = require("../controller/auth");
const
  isRequestValidated,
  validateSignUpRequest,
  validateSignIpRequest,
} = require("../validators/auth");


router.route("/signin").post(validateSignIpRequest, isRequestValidated, signIn);


router.route("/signup").post(validateSignUpRequest, isRequestValidated, signUp);


module.exports = router;

So in this code, we import the router by using express, and by using that we make out API endpoint.

The router has one route method when we can make our APIs endpoint as we added in upper code according to our APIs. For example, for Sign In API, we add “/sign in,” and for SignUp API, we add “/signup.”

After that, we have to define methods of APIs like get, post, put, patch, etc. then we will use a validator which is imported from the validators folder, and after the validator, at last, we put our controller where we will write our APIs logic.

Let me explain to you from scratch when the user does sign in with the credentials like email and password and hits the button at a time sign-in API call, and before going to the controller, in the router, it will check validation through our validator.

So if there is anything wrong with the credential, like email format or password length, then it will break this call from here and respond to the user with an error message which you can check on the validation file where we wrote code for it.

If everything is fine, the call will go into the controller, and now we will write a code for it.

🔹Sixth Step

Make an auth.js file in the controller folder and write the below code.

const { StatusCodes } = require("http-status-codes");
const User = require("../models/auth");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
const shortid = require("shortid");
const signUp = async (req, res) => {
  const { firstName, lastName, email, password } = req.body;
  if (!firstName || !lastName || !email || !password) {
     return res.status(StatusCodes.BAD_REQUEST).json({
        message: "Please Provide Required Information",
     });
  }

  const hash_password = await bcrypt.hash(password, 10);
 
  const userData = {
     firstName,
     lastName,
     email,
     hash_password,
  };

  const user = await User.findOne({ email });
  if (user) {
     return res.status(StatusCodes.BAD_REQUEST).json({
        message: "User already registered",
     });
  } else {
     User.create(userData).then((data, err) => {
     if (err) res.status(StatusCodes.BAD_REQUEST).json({ err });
     else
       res
        .status(StatusCodes.CREATED)
        .json({ message: "User created Successfully" });
     });
  }
};
const signIn = async (req, res) => {
  try {
     if (!req.body.email || !req.body.password) {
        res.status(StatusCodes.BAD_REQUEST).json({
           message: "Please enter email and password",
        });
     }
 
     const user = await User.findOne({ email: req.body.email });
    
     if (user) {
     if (user.authenticate(req.body.password)) {
           const token = jwt.sign(
              { _id: user._id, role: user.role },
              process.env.JWT_SECRET,{ expiresIn: "30d"});
  const { _id, firstName, lastName, email, role, fullName } = user;
  res.status(StatusCodes.OK).json({
       token,
       user: { _id, firstName, lastName, email, role, fullName },
  });
 } else {
  res.status(StatusCodes.UNAUTHORIZED).json({
     message: "Something went wrong!",
  });
 }
} else {
  res.status(StatusCodes.BAD_REQUEST).json({
      message: "User does not exist..!",
  });
}
} catch (error) {
   res.status(StatusCodes.BAD_REQUEST).json({ error });
  }
};
module.exports = { signUp, signIn};

First, we will talk about using API; you can see the code when the user does signup with an email, password, first name, and last name. If this request parameter is not found will return an error message.

If the user requests all required data, API does the further task and generates a hash password using the bcrypt package.

Then we try to find an email using findOne in the database. If we get it, we send a response with the message “Email address is already used”; we add that user to the database.

Here, we are using the User schema we made in the Modal folder. This schema gives us many methods to play with databases, like create, find, delete and update.

Now, we will talk about signing API. We need only two pieces of data from the user side, one is an email, and the second is a password. If one of these data is missing, then break this API and send a response with an error message and appropriate status code.

After that, we find that user in the database by email address. If we get that user, we do the other process for authentication. Otherwise, we break this API from here and send a response with an error message like “user does not exist.”

If we get the user from the database, then we cross-check the password that the user gives. You can see the method we access it from the user and write it in the user schema. If the password matches the database, generate one token with an expiration time. For that, we are using jsonwebtoken package to develop it and send a successful response with the token.

Now your APIs are ready to use, and make sure one thing is that when you use APIs, your backend server needs to start when you use it locally. APIs look like
SIGN IN: http://localhost:2000/api/signin
SIGN UP: http://localhost:2000/api/signup

coma

Conclusion

In this blog, we learn how to make APIs for registration and login form using Node.js and MongoDB, generate tokens, create a hash password, connect to the DB, and make schema, controller, router, and all those things.

Keep Reading

Keep Reading

Struggling with EHR integration? Learn about next-gen solutions in our upcoming webinar on Mar 6, at 11 AM EST.

Register Now

Let's create something together!