ExpressJS Login Validation Error: comparePassword is not a function

Issue

In the folder backend/controllers I have an authController class which handles a POST request with an email and password and then looks for a user in the database with a matching email, then when it finds the user, it tries to verify the password invoking a method called comparePassword which is defined in the user class located in backend/models.

The authController class:

const User = require('../models/user');
const ErrorHandler = require('../utils/errorHandler');
const catchAsynErrors = require('../middlewares/catchAsyncErrors');
const sendToken = require('../utils/jwtToken');


// register user => /api/v1/register
exports.registerUser = catchAsynErrors(async (req, res, next)=> {
const { name, email, password} = req.body;

const user = await User.create({
    name,
    email, 
    password,
    avatar: {
        public_id: '',
        url: ''
    }
})

sendToken(user, 200, res)
})



// login user => /a[i/v1/login
exports.loginUser = catchAsynErrors(async(req, res, next)=> {
    const { email, password} = req.body;

    // checks if email  and password is entered by the user
    if(!email || !password){
        return next(new ErrorHandler('Please enter email & password', 400))
    }

    //finding user in database
    const user = await User.find( { email } ).select('+password')

    if(!user){
        return next(new ErrorHandler('Invalid Email or Password', 401))
    }
    
    // Attempting to print the user object's functions
    console.log(user.comparePassword); // undefined
    console.log(user.find); // [Function: find]
    
    //checks if password is correct or not 
    const isPasswordMatched = await user.comparePassword(password);
    
    if(!isPasswordMatched){
        return next(new ErrorHandler('Invalid Email or Password', 401))
    }
    sendToken(user, 200, res)   
});

The user class:

const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

const userSchema  = new mongoose.Schema({
name: {
    type: String,
    required: [true, 'Please enter your name'],
    maxLenght: [30, 'Your name cannot exceed 30 characters']
},
email:{
    type: String,
    required: [true, 'Please enter your email'],
    unique: true, 
    validate: [validator.isEmail, 'Please enter a valid email address']
}, 
password:{
    type: String,
    required: [true, 'Please enter your password'],
    minlenght: [6, 'Your password must have at least 6 characters'],
    select: false
},
avatar:{
    public_id: {
        type: String,
        required: true
    },
    url:{
        type: String,
        required: true
    }

},
role:{
    type: String,
    default: 'user'
},
createdAt:{
    type: Date,
    default: Date.now
},
resetPasswordToken: String,
    resetPasswordExpire: Date
})

//encrypting password before saving user
userSchema.pre('save', async function (next){
    if(!this.isModified('password')){
        next()
    }
    this.password = await  bcrypt.hash(this.password, 10)
})

// compare user password
userSchema.methods.comparePassword = async function (enteredPassword) {
    return await bcrypt.compare(enteredPassword, this.password)
}


// return jwt token 
userSchema.methods.getJwtToken = function (){
    return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
        expiresIn: process.env.JWT_EXPIRES_TIME
    });
}
module.exports = mongoose.model('User', userSchema);

And this is the result I get when I try to make a POST request with Postman
along with what the console (in this case, via the VS Code Terminal) shows upon trying to console.log the function comparePassword alongside the function find, both of which belong on the same class.

Postman:

{
    "success": false,
    "error": {
        "statusCode": 500
    },
    "errMessage": "user.comparePassword is not a function",
    "stack": "TypeError: user.comparePassword is not a function\n    at D:\\pruebas de programaciĆ³n\\Proyectazo\\backend\\controllers\\authController.js:47:42\n    at processTicksAndRejections (node:internal/process/task_queues:96:5)"
}

Console:

PS D:\pruebas de programaciĆ³n\Proyectazo> npm run dev
npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead.

> [email protected] dev
> SET NODE_ENV=DEVELOPMENT& nodemon backend/server

[nodemon] 2.0.19
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json  
[nodemon] starting `node backend/server.js` 
Server started on PORT: 4000 in DEVELOPMENT mode. 
MongoDB Database connected with HOST: localhost
undefined
[Function: find]

I’ve made it this far following this tutorial https://www.youtube.com/watch?v=_zXBZS6E-jM&list=PLkVd4_IMjZgkwcXwnpy7tenGNBbRdiRO8&index=27 and it appears to work perfectly in the video. I looked up other solutions, some of them even within StackOverflow, and they all look like variants or refactors of the same implementation that I have. Any ideas?

Thanks in advance!

Solution

The reason it’s not working because in loginUser function

//finding user in database
    const user = await User.find( { email } ).select('+password')

user is an array.
Instead use Use.findOne() method

// This will return single user object
        const user = await User.findOne( { email } ).select('+password')

Answered By – Neeraj Kumar

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published