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 functionn at D:\pruebas de programaciĆ³n\Proyectazo\backend\controllers\authController.js:47:42n at processTicksAndRejections (node:internal/process/task_queues:96:5)" }
Console:
PS D:pruebas de programaciĆ³nProyectazo> npm run dev npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead. > proyectazo@1.0.0 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!
Advertisement
Answer
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')