req.body giving an empty object {} only in case of api/vi/user/login route

req.body is empty in Node.js Express API route when sending JSON request using Postman.

This is the error:

<pre>Error: Username or email is required<br> &nbsp; &nbsp;at file:///C:/Users/hrshl/OneDrive/Desktop/Harshal/Web%20dev/vdeohub-backend/src/controllers/user.controller.js:117:15</pre>

This part is causing the error:

if (!username && !email) {
        throw new ApiError(400, 'Username or email is required');
}

This is the loginUser function which was giving the error:

const loginUser = asyncHandler(async (req, res) => {
    // Take the data from the frontend - username/email and password
    // Validate the user details - if all fields are present or not
    // Check if the username/email exists in database
    // If yes, check if the password is correct or not using isPasswordCorrect method
    // create an access token and a refresh token+
    const generateAccessAndRefreshTokens = async (userId) => {
        try {
            user = User.findById(userId);
            const accessToken = user.generateAccessToken();
            const refreshToken = user.generateRefreshToken();

            user.refreshToken = refreshToken;
            user.save({ validateBeforeSave: false });

            return { accessToken, refreshToken };
        } catch (error) {
            return new ApiError(
                500,
                'Something went wrong while generating access and refresh tokens'
            );
        }
    };

    const { username, email, password } = req.body;

    // console.log(req.body);    

    if (!username && !email) {
        throw new ApiError(400, 'Username or email is required');            //error from here
    }

    const user = await User.findOne({
        $or: [{ username }, { email }],
    });

    if (!user) {
        return new ApiError(404, 'User does not exists');
    }

    if (!password) {
        return new ApiError(400, 'Password is required');
    }

    const isPasswordValid = user.isPasswordCorrect(password);

    if (!isPasswordValid) {
        throw new ApiError(401, 'Invalid user credentials');
    }

    const { accessToken, refreshToken } = await generateAccessAndRefreshTokens(
        user._id
    );

    const loggedInUser = User.findById(user._id).select(
        '-password -refreshToken'
    );

    options = {
        httpOnly: true,
        secure: true,
    };
    

    return res
        .status(200)
        .cookie('refreshToken', refreshToken, options)
        .cookie('accessToken', accessToken, options)
        .json(
            new ApiResponse(
                200,
                { user: loggedInUser, accessToken, refreshToken },
                'User logged in successfully'
            )
        );
});

This is the asyncHandler utility:

const asyncHandler = (requestHandler) => {
    return (req, res, next) => {
        Promise.resolve(requestHandler(req, res, next)).catch((err) =>
            next(err)
        );
    };
};

export { asyncHandler };

This is the user model:

import mongoose from 'mongoose';
import bcrypt from 'bcrypt';
import JWT from 'jsonwebtoken';

const userSchema = new mongoose.Schema(
    {
        username: {
            type: String,
            required: [true, 'Username is required...'],
            unique: true,
            lowercase: true,
            trim: true,
            index: true,
        },
        email: {
            type: String,
            required: [true, 'Email is required...'],
            unique: true,
            lowercase: true,
            trim: true,
        },
        fullName: {
            type: String,
            required: [true, 'Full name is required...'],
            trim: true,
            index: true,
        },
        avatar: {
            type: String, //Cloudinary URL
            required: [true, 'Avatar is required...'],
        },
        coverImage: {
            type: String, //Cloudinary URL
        },
        watchHistory: [
            {
                type: mongoose.Schema.Types.ObjectId,
                ref: 'Video',
            },
        ],
        password: {
            type: String,
            required: [true, 'Password is required...'],
            minlength: [6, 'Password must be at least 6 characters...'],
        },
    },
    {
        timestamps: true,
    }
);

userSchema.pre('save', async function (next) {
    if (this.isModified('password')) {
        this.password = await bcrypt.hash(this.password, 10);
    }
    next();
});

userSchema.methods.isPasswordCorrect = async function (password) {
    return await bcrypt.compare(password, this.password);
};

userSchema.methods.generateAccessToken = function () {
    return JWT.sign(
        {
            _id: this._id,
        },
        process.env.ACCESS_TOKEN_SECRET,
        {
            expiresIn: process.env.ACCESS_TOKEN_EXPIRY,
        }
    );
};

userSchema.methods.generateRefreshToken = function () {
    return JWT.sign(
        {
            _id: this._id,
        },
        process.env.REFRESH_TOKEN_SECRET,
        {
            expiresIn: process.env.REFRESH_TOKEN_EXPIRY,
        }
    );
};

export const User = mongoose.model('User', userSchema);

If you need to look at the entire codebase, head to my GitHub repo.

There is also a registerUser function, and it also takes in arguments from req.body, but that one works perfectly fine:

const registerUser = asyncHandler(async (req, res) => {
    // get user details from frontend
    // validate the user details - if all the fields are present or not
    // check if user already exixts in database - username, email
    // if user exists send error response to frontend
    // check if the user has uploaded a profile picture/avatar
    // if yes, upload the image to cloudinary
    // check for the image uploaded to cloudinary successfully or not
    // if yes, save the user details to a new user object
    // create a new user in the database
    // remove the password and the refreshToken from the response
    // check for user creation success or failure
    // if success, send the response to the frontend

    const { fullName, email, username, password } = req.body;
    console.log(req.body);

    if (
        [fullName, email, username, password].some(
            (field) => field?.trim() === ''
        )
    ) {
        throw new ApiError(400, 'All fields are required');
    }

    const existedUser = await User.findOne({
        $or: [{ email }, { username }],
    });

    if (existedUser) {
        throw new ApiError(409, 'User already exists');
    }

    console.log(req.files);

    const avatarLocalPath = req.files?.avatar[0]?.path;
    const coverImageLocalPath = req.files?.coverImage?.[0]?.path;

    if (!avatarLocalPath) {
        throw new ApiError(
            400,
            'Avatar is required. Avatar not found in request body'
        );
    }

    const avatar = await uploadToCloudinary(avatarLocalPath);

    let coverImage;
    if (!(coverImageLocalPath === undefined)) {
        coverImage = await uploadToCloudinary(coverImageLocalPath);
    }

    if (!avatar) {
        throw new ApiError(
            400,
            'Avatar is required. Failed to upload avatar to cloudinary'
        );
    }

    const user = await User.create({
        fullName: fullName,
        email: email,
        username: username.toLowerCase(),
        password: password,
        avatar: avatar.url,
        coverImage: coverImage?.url || '',
    });

    const createdUser = await User.findById(user._id).select(
        '-password -refreshToken'
    );

    if (!createdUser) {
        throw new ApiError(500, 'Failed to create user. Something went wrong.');
    }

    return res
        .status(201)
        .json(new ApiResponse(200, createdUser, 'User created successfully'));
});

I tried console logging the entire request, and it was fine, just the request.body was empty. I also tried adding bodyparser, but it didn’t do anything. I also tried changing the check to:

if (!(username || email)) {
        throw new ApiError(400, 'Username or email is required');
}

but still no luck.