Middleware

Middleware are functions which are passed control of flow during execution of document init, validate, save and remove methods. Middleware are executed at the document level, not the Model level. There are two types of middleware, pre and post. Let’s start with pre.

Pre

There are two types of pre middleware, serial and parallel.

Serial

Serial middleware are executed one after another, when each middleware calls next.

  1. var schema = new Schema(..);
  2. schema.pre('save', function (next) {
  3. // do stuff
  4. next();
  5. });

Parallel

Parallel middleware offer more fine-grained flow control.

  1. var schema = new Schema(..);
  2. schema.pre('save', true, function (next, done) {
  3. // calling next kicks off the next middleware in parallel
  4. next();
  5. doAsync(done);
  6. });

The hooked method, in this case save, will not be executed until done is called by each middleware.

Use Cases

Middleware are useful for atomizing model logic and avoiding nested blocks of async code. Here are some other ideas:

  • complex validation
  • removing dependent documents
    • (removing a user removes all his blogposts)
  • asynchronous defaults
  • asynchronous tasks that a certain action triggers
    • triggering custom events
    • notifications

Error handling

If any middleware calls next or done with an Error instance, the flow is interrupted, and the error is passed to the callback.

  1. schema.pre('save', function (next) {
  2. var err = new Error('something went wrong');
  3. next(err);
  4. });
  5. // later...
  6. myDoc.save(function (err) {
  7. console.log(err.message) // something went wrong
  8. });

Post middleware

post middleware are executed after the hooked method and all of its pre middleware have completed. post middleware do not directly receive flow control, e.g. no next or done callbacks are passed to it. post hooks are a way to register traditional event listeners for these methods.

  1. schema.post('init', function (doc) {
  2. console.log('%s has been initialized from the db', doc._id);
  3. })
  4. schema.post('validate', function (doc) {
  5. console.log('%s has been validated (but not saved yet)', doc._id);
  6. })
  7. schema.post('save', function (doc) {
  8. console.log('%s has been saved', doc._id);
  9. })
  10. schema.post('remove', function (doc) {
  11. console.log('%s has been removed', doc._id);
  12. })

Notes on findAndUpdate()

`pre` and `post` are not called for update operations executed directly on the database, including `Model.update`,`.findByIdAndUpdate`,`.findOneAndUpdate`, `.findOneAndRemove`,and `.findByIdAndRemove`.order to utilize `pre` or `post` middleware, you should `find()` the document, and call the `init`, `validate`, `save`, or `remove` functions on the document. See [explanation](http://github.com/LearnBoost/mongoose/issues/964).

Next Up

Now that we’ve covered middleware, let’s take a look at Mongoose’s approach to faking JOINs with its query population helper.