DEV Community

Manthan Ankolekar
Manthan Ankolekar

Posted on

Building an OTP Verification System with Node.js and MongoDB

In today's digital world, security is paramount, especially when it comes to user authentication. One-time passwords (OTPs) are widely used to verify user identities during online transactions, account logins, and more. In this blog, we'll explore how to implement an OTP verification system using Node.js, Express.js, and MongoDB.

Understanding the Components

Before diving into the implementation details, let's understand the various components of our OTP verification system:

  1. Express.js: A fast, unopinionated, minimalist web framework for Node.js. We'll use Express.js to handle HTTP requests and responses.

  2. MongoDB: A popular NoSQL database used for storing and retrieving OTP data.

  3. Nodemailer: A module for Node.js applications that allows easy email sending.

  4. Randomstring: A library to generate random strings, which we'll use to create OTPs.

Project Structure Overview

├── controllers/
│   └── otpController.js
├── models/
│   └── otpModel.js
├── routes/
│   └── otpRoutes.js
├── utils/
│   └── sendEmails.js
├── index.js
├── .env
└── package.json
Enter fullscreen mode Exit fullscreen mode

Understanding the Project Structure

  1. controllers/: This directory contains the controller functions responsible for handling the business logic of our OTP verification system. In this case, otpController.js holds the logic for sending and verifying OTPs.

  2. models/: Here, we define the data models for our MongoDB database. The otpModel.js file specifies the schema for storing OTPs.

  3. routes/: This directory houses the route definitions for our Express.js application. The otpRoutes.js file specifies the endpoints for sending and verifying OTPs.

  4. utils/: This directory contains utility functions used within our application. The sendEmails.js file handles email sending using Nodemailer.

  5. index.js: This is the entry point of our application where we set up our Express server, configure middleware, connect to the MongoDB database, and define route handling.

  6. .env: This file stores environment variables such as database credentials, SMTP configuration, and any other sensitive information. We use the dotenv package to load these variables into our application.

  7. package.json: This file contains metadata about our project and lists the dependencies required for our application. It also includes scripts for running and testing our application.

Environment Variables in .env

Enter fullscreen mode Exit fullscreen mode

Setting Up the Environment

First, let's set up our development environment. Make sure you have Node.js and npm installed on your system. Then, create a new directory for your project and initialize it with npm:

mkdir otp-generator
cd otp-generator
npm init -y
Enter fullscreen mode Exit fullscreen mode

Now, install the required dependencies:

npm install express cors body-parser mongoose nodemailer randomstring dotenv
Enter fullscreen mode Exit fullscreen mode

Implementing the OTP Verification System

Now, let's implement the OTP verification system step by step.

Database Setup: We'll start by setting up our MongoDB database and defining the OTP schema.

// models/otpModel.js
const mongoose = require('mongoose');

const otpSchema = new mongoose.Schema({
    email: { type: String, required: true },
    otp: { type: String, required: true },

const Otps = mongoose.model('otps', otpSchema);

module.exports = Otps;
Enter fullscreen mode Exit fullscreen mode

Creating Routes: Next, we'll define the routes for sending and verifying OTPs.

// routes/otpRoutes.js
const express = require('express');
const { sendOTP, verifyOTP } = require('../controllers/otpController');

const router = express.Router();

router.get('/sendOTP', sendOTP);
router.get('/verifyOTP', verifyOTP);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Implementing Controller Logic: Now, let's implement the logic for sending and verifying OTPs.

// controllers/otpController.js
const Otps = require('../models/otpModel.js');
const randomstring = require('randomstring');
const sendEmail = require('../utils/sendEmails');

// Generate OTP
function generateOTP() {
    return randomstring.generate({
        length: 6,
        charset: 'numeric'

// Send OTP to the provided email
exports.sendOTP = async (req, res, next) => {
    try {
        const { email } = req.query;
        const otp = generateOTP(); // Generate a 6-digit OTP
        const newOTP = new Otps({ email, otp });

        // Send OTP via email
        await sendEmail({
            to: email,
            subject: 'Your OTP',
            message: `<p>Your OTP is: <strong>${otp}</strong></p>`,

        res.status(200).json({ success: true, message: 'OTP sent successfully' });
    } catch (error) {
        console.error('Error sending OTP:', error);
        res.status(500).json({ success: false, error: 'Internal server error' });

// Verify OTP provided by the user
exports.verifyOTP = async (req, res, next) => {
    try {
        const { email, otp } = req.query;
        const existingOTP = await Otps.findOneAndDelete({ email, otp });

        if (existingOTP) {
            // OTP is valid
            res.status(200).json({ success: true, message: 'OTP verification successful' });
        } else {
            // OTP is invalid
            res.status(400).json({ success: false, error: 'Invalid OTP' });
    } catch (error) {
        console.error('Error verifying OTP:', error);
        res.status(500).json({ success: false, error: 'Internal server error' });
Enter fullscreen mode Exit fullscreen mode

Configuring Email Sending: Finally, we'll configure Nodemailer to send emails containing the OTP.

// utils/sendEmails.js
const nodeMailer = require("nodemailer");

const sendEmail = async (options) => {
    const transporter = nodeMailer.createTransport({
        host: process.env.SMPT_HOST,
        port: process.env.SMPT_PORT,
        secure: true, // Use SSL
        auth: {
            user: process.env.SMPT_MAIL,
            pass: process.env.SMPT_APP_PASS,
        authMethod: 'LOGIN', // Specify the authentication method

    const mailOptions = {
        from: process.env.SMPT_MAIL,
        subject: options.subject,
        html: options.message,

    await transporter.sendMail(mailOptions);

module.exports = sendEmail;
Enter fullscreen mode Exit fullscreen mode

Setting Up Express Server: Finally, we'll set up our Express server to listen to incoming requests.

// index.js
const express = require("express");
const app = express();
const PORT = process.env.PORT || 3000;
const cors = require("cors");
const bodyParser = require("body-parser");
const mongoose = require('mongoose');

const otpRoutes = require('./routes/otpRoutes');


const dbUser = process.env.MONGODB_USER;
const dbPassword = process.env.MONGODB_PASSWORD;

        { useNewUrlParser: true, useUnifiedTopology: true } // Add these options for MongoDB connection
    .then(() => {
        console.log("Connected to MongoDB database!");
    .catch((error) => {
        console.error("Connection failed!", error); // Log the error for better debugging

        origin: "*",

app.use('/api', otpRoutes);

app.listen(PORT, () => {
    console.log(`Server started on port ${PORT}`);
Enter fullscreen mode Exit fullscreen mode


Congratulations! You've successfully implemented an OTP verification system using Node.js, Express.js, and MongoDB. This system enhances the security of your application by adding an extra layer of authentication. Feel free to customize and extend this system to meet the specific requirements of your project.

Exploring the Code

Visit the GitHub repository to explore the code in detail.

Feel free to customize the blog according to your preferences and provide more details or explanations where needed.

Top comments (0)