1

I am using findOneAndUpdate method of Mongoose to handle put requests for my restful API. I leave the password to the second step to use 'save' pre hook of Mongoose, and I am hashing the password in the pre hook.

router.put("/:id", function(req, res, next){

  var password = req.body["password"];
  delete req.body["password"];

  var p = new Promise(function (resolve, reject){
    User.findOneAndUpdate({_id:req.params.id}, req.body, { 'new': true } ,function (err, user) {
      if (err) {
        reject(err);
      }
      else {
        resolve(user);
      }
    });
  });

  p.then(function(user){
    if(!password) {
      return user;
    }
    user.password = password;
    user.save(function(err) {
      if (err) {
        return(err);
      }
      else {
        console.log(user); // The object is updated and here, but in postman I have "No Data Received"
        return(user);
      }
    });
  }).then(function(result){
    return res.json(result);
  }).catch(function(err){
    next(err);
  });
});

In the comment above, I have the user object in that block. However in Postman, I have "No Data Received". Any thoughts?

Hasan Can Saral
  • 2,950
  • 5
  • 43
  • 78
  • 2
    Your `user.save` is inside a callback - you need to promisify that - Mongoose has promise support though so no need for explicit creation. – Benjamin Gruenbaum Feb 24 '16 at 19:21
  • 1
    You're trying to `return` from an asynchronous callback. That doesn't work with non-promises. – Bergi Feb 24 '16 at 19:22
  • @BenjaminGruenbaum Thanks for comments, I am quite new to this, so could not figure out how to promisify user.save inside the callback. Any help would be appreciated. Thanks! – Hasan Can Saral Feb 24 '16 at 19:48
  • 1
    http://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises always convert at the lowest level possible - also if you use a library like bluebird automatic promisification of all properties is a one liner. – Benjamin Gruenbaum Feb 24 '16 at 19:49

1 Answers1

1

Even better, use mongoose pre-save hooks.

UserSchema.pre('save', true, function beforeSave (next, done) {
  let user = this;

  if (this.isModified('password')) {
    bcrypt.genSalt(10, function genSalt (err, salt) {
      if (err) {
        return done(err);
      }

      bcrypt.hash(user.password, salt, function hash (err, hash) {
        if (err) {
          return done(err);
        }

        user.password = hash;
        done();
      });
    });
  } else {
    done();
  }

  next();
});

Which will allow you to save the user and if password hasn't been modified won't update it.

router.put('/:id', function (req, res, next) {
  User.findById(req.params.id).exec(function (err, user) {
    user.set(req.body);
    user.save(function (err) {
      if (err) {
        return next(err);
      }

      return res.json(user);
  });
});
Mike
  • 1,442
  • 10
  • 20
  • Doesn't `save()` return a promise as well? Then why don't you use that? – Bergi Feb 24 '16 at 20:01
  • The reason why I was trying to do this was because I had a pre-hook but it doesn't work with findOneAndUpdate. So the second question quite answer my original problem. However, the first part doesn't seem to resolve/return the promise, stuck in Postman too. – Hasan Can Saral Feb 24 '16 at 20:04
  • @Bergi I guess it does. I have been using Mongoose for a long time and `model.save()` used to not return a `Promise`. – Mike Feb 24 '16 at 20:06
  • @HasanCanSaral I haven't tested this code, so I wouldn't copy and paste it directly. If you read the `mongoose` docs they recommend doing a `findOne` followed by `save` if you need hooks. It works out better in the long run. – Mike Feb 24 '16 at 20:10
  • @Mike I would be happy to (and want to) accept the answer if you could remove the first part, which is not working. – Hasan Can Saral Feb 26 '16 at 21:38
  • @HasanCanSaral removed it. – Mike Feb 28 '16 at 16:37