Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose pre.save() async middleware not working as expected

Tags:

Following up from : Mongoose unique validation error type

I'm using this schema with mongoose 3.0.3 from npm:

var schema = new Schema({      _id: Schema.ObjectId,     email: {type: String, required: true, unique: true}  }); 

With this middleware to get a validationError from unique:true

schema.pre("save", function(next, done) {     var self = this;      model.findOne({email : this.email}, 'email', function(err, results) {         if(err) {             done(err);         } else if(results) {             console.warn('results', results);             self.invalidate("email", "email must be unique");             done(new Error("email must be unique"));         } else {             done();         }     });      next(); }); 

However, it does not work!

Users.create() will still return a a MongoError: E11000 duplicate key error index and the console.warn() is only called after that.

The save action should not be called until all middleware done()have been called according to the docs, and I should get back a validation error.

It looks like the done() behavior is not working as expected,

Any idea why?

like image 538
Olivier Avatar asked Nov 27 '12 11:11

Olivier


People also ask

Is Mongoose save async?

save() is a method on a Mongoose document. The save() method is asynchronous, so it returns a promise that you can await on.

What is pre save in Mongoose?

The save() function triggers validate() hooks, because mongoose has a built-in pre('save') hook that calls validate() . This means that all pre('validate') and post('validate') hooks get called before any pre('save') hooks.

Does Mongoose save return a promise?

While save() returns a promise, functions like Mongoose's find() return a Mongoose Query . Mongoose queries are thenables. In other words, queries have a then() function that behaves similarly to the Promise then() function. So you can use queries with promise chaining and async/await.

Does Mongoose save overwrite?

Mongoose save with an existing document will not override the same object reference. Bookmark this question.


1 Answers

You're using a parallel middleware callback function (with both next and done), but you're not setting the parallel flag in the schema.pre parameters so it's using the serial rules.

So either include the parallel flag in your call:

schema.pre("save", true, function(next, done) { ... 

Or switch to a serial middleware callback style if that's all you need anyway:

schema.pre("save", function(next) {     var self = this;      model.findOne({email : this.email}, 'email', function(err, results) {         if(err) {             next(err);         } else if(results) {             console.warn('results', results);             self.invalidate("email", "email must be unique");             next(new Error("email must be unique"));         } else {             next();         }     }); }); 
like image 173
JohnnyHK Avatar answered Sep 19 '22 17:09

JohnnyHK