Search code examples
node.jsmongodbexpress

How to handle multiple requests (Express.js) with mongodb insert operation


Context

I am following a tutorial which is titled "Building a Secure User Registration and Login API with Express.js ,MongoDB and JWT"

It uses express bcryptjs jsonwebtoken mongoose to build a simple web server with register/login functionality.

The problem

The problem arise when I send a multitude of requests to /api/register. Server creates many users with same credentials.

Code to reproduce

Server

// Importing required modules
const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

// Creating an Express application instance
const app = express();
const PORT = 3000;

// Connect to MongoDB database
mongoose.connect('mongodb://localhost:27017/mydatabase')
    .then(() => {
        console.log('Connected to MongoDB');
    })
    .catch((error) => {
        console.error('Error connecting to MongoDB:', error);
    });

// Define a schema for the User collection
const userSchema = new mongoose.Schema({
    username: String,
    email: String,
    password: String
});

// Create a User model based on the schema
const User = mongoose.model('User', userSchema);

// Middleware to parse JSON bodies
app.use(express.json());

// Route to register a new user
app.post('/api/register', async (req, res) => {
    try {
        // Check if the email already exists
        const existingUser = await User.findOne({email: req.body.email});
        if (existingUser) {
            return res.status(400).json({error: 'Email already exists'});
        }

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

        // Create a new user
        const newUser = new User({
            username: req.body.username,
            email: req.body.email,
            password: hashedPassword
        });

        await newUser.save();
        console.log(newUser)
        res.status(201).json({message: 'User registered successfully'});
    } catch (error) {
        console.log('Internal server error')
        res.status(500).json({error: 'Internal server error'});
    }
});

// Start the server
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

Client

(() => {
    const send = () => fetch('http://127.0.0.1:3000/api/register', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            "username": "testuser", "email": "[email protected]", "password": "testpassword"
        })
    }).then()

    for (let i = 0; i < 10; i++) send()
})()

Output

{
  username: 'testuser',
  email: '[email protected]',
  password: '$2a$10$5Jo01CQ797EuMrujH49Of.OmFY4n6yIX.4VgU8FOEhXRmr.CxHnRy',
  _id: new ObjectId('668cf24a08fa6ed9fbf442d5'),
  __v: 0
}
...9 more users created

Solution

  • This is a common issue with asynchronous operations. It is like 10 people asked at the same time if there is a chair available. All 10 of them gets the save answer yes but only one of them can sit on it.

    In order to solve this, you should validation to the model

    const userSchema = new mongoose.Schema({
      username: { type: String, unique: true },
      email: { type: String, unique: true },
      password: String,
    });