Home > Software design >  Why do I get an error when I try to save mongoose model?
Why do I get an error when I try to save mongoose model?

Time:10-29

I am trying to create controller for resetting user password in Node.JS. The idea is to fetch the user from DB based on reset password token, do some validation update the relevant field and save it back to the DB. However, I get an error when trying to save the updated user ("user.save is not a function"). What might be the reason?

I have a user model defined as follows:

const mongoose = require("mongoose");
const validator = require("validator");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, "Please enter valid name"],
    maxLength: [30, "Your name cannot exceed 30 characters]"],
  },
  email: {
    type: String,
    required: [true, "Please enter valid email"],
    unique: true,
    validate: [validator.isEmail, "Please enter valid email address"],
  },
  password: {
    type: String,
    requires: [true, "Please enter your password"],
    minLength: [6, "Your password must be 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: new Date().now },
  resetPasswordToken: { type: String },
  resetPasswordExpire: { type: Date },
});

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

// check password matching
userSchema.methods.isPasswordMatched = async function (inputPassword) {
  return await bcrypt.compare(inputPassword, this.password);
};

// return JSON Web Token
userSchema.methods.getJwtToken = function () {
  return jwt.sign({ id: this.id }, process.env.JWT_SECRET, {
    expiresIn: process.env.JWT_EXPIRESIN_TIME,
  });
};

// Generate password token
userSchema.methods.getResetPasswordToken = function () {
  // Generate Token
  const resetToken = crypto.randomBytes(20).toString("hex");

  // Hash token
  this.resetPasswordToken = crypto
    .createHash("sha256")
    .update(resetToken)
    .digest("hex");

  // set expired time
  this.resetPasswordExpire = new Date(Date.now()   30 * 60 * 1000);

  return resetToken;
};

module.exports = mongoose.model("User", userSchema);

When I try to reset user password I try the following:

  // get the user document from db (make sure token and expiration time are valid)
let user = User.findOne({
    resetPasswordToken: resetPasswordToken,
    resetPasswordExpire: { $gt: Date.now() },
  });

// update password
  user.password = req.body.password;
  user.resetPasswordToken = undefined;
  user.resetPasswordExpire = undefined;
  user.save();
  sendToken(user, 200, res);

for some reason I get an error: "errorMsg": "user.save is not a function"

What might be the problem?

CodePudding user response:

use await keyword to get mongoose hydrating the response

let user = await User.findOne({
        resetPasswordToken: resetPasswordToken,
        resetPasswordExpire: { $gt: Date.now() },
      });
    
    // update password
      user.password = req.body.password;
      user.resetPasswordToken = undefined;
      user.resetPasswordExpire = undefined;
      user.save();
      sendToken(user, 200, res);

or you can do it like this

await User.findOneAndUpdate({
        resetPasswordToken: resetPasswordToken,
        resetPasswordExpire: { $gt: Date.now() },
      },{password : req.body.password, resetPasswordToken : undefined, 
      resetPasswordExpire : undefined,});

CodePudding user response:

Probably, user is null or undefined, so you should handle the user null condition. Also findOne and save returns promise, so you need to add await keyword before them.

Also you have a typo in user schema password field, requires should be required .

let user = await User.findOne({
  resetPasswordToken: resetPasswordToken,
  resetPasswordExpire: { $gt: Date.now() },
});

if (user) {
  // update password
  user.password = req.body.password;
  user.resetPasswordToken = undefined;
  user.resetPasswordExpire = undefined;
  await user.save();
  sendToken(user, 200, res);
} else {
  res.status(400).send("No user found");
}

If you get the user null, you need to fix your query in findOne.

  • Related