To make MongoDB database manipulation easy, we can use the Mongoose NPM package to make working with MongoDB databases easier.
In this article, we’ll look at how to use Mongoose to manipulate our MongoDB database.
Save/Validate Hooks
The save method will trigger validate hooks.
This is because Mongoose calls the pre('save') hook that calls validate .
The pre('validate') and post('validate') hooks are called before any pre('save') hooks.
For example, we can write:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({ name: String });
schema.pre('validate', () => {
console.log('1');
});
schema.post('validate', () => {
console.log('2');
});
schema.pre('save', () => {
console.log('3');
});
schema.post('save', () => {
console.log('4');
});
const User = connection.model('User', schema);
new User({ name: 'test' }).save();
}
run();
to add the schema hooks.
Then they’ll be called one by one in the same order that they’re listed.
Query Middleware
Pre and post save hooks aren’t run when update methods are run.
For example, if we have:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({ name: String });
schema.pre('updateOne', { document: true, query: false }, function() {
console.log('Updating');
});
const User = connection.model('User', schema);
const doc = new User();
await doc.updateOne({ $set: { name: 'test' } });
await User.updateOne({}, { $set: { name: 'test' } });
}
run();
Then when we have query set to false or didn’t add the query property, then the updateOne pre hook won’t run when we run updateOne .
Aggregation Hooks
We can add aggregation hooks.
For example, we can write:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({
name: {
type: String,
unique: true
}
});
schema.pre('aggregate', function() {
this.pipeline().unshift({ $match: { isDeleted: { $ne: true } } });
});
const User = connection.model('User', schema);
}
run();
We listen to the aggregate event.
Error Hooks
We can get errors from hooks.
For example, we can write:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({
name: {
type: String,
unique: true
}
});
schema.post('update', function (error, res, next) {
if (error.name === 'MongoError' && error.code === 11000) {
next(new Error('There was a duplicate key error'));
} else {
next();
}
});
const User = connection.model('User', schema);
}
run();
We listen to the update event and get the error from the error parameter.
We can get the name and code to get information about the error.
Synchronous Hooks
Some hooks are always synchronous.
init hooks are always synchronous because the init function is synchronous.
For example, if we have:
async function run() {
const { createConnection, Schema } = require('mongoose');
const connection = createConnection('mongodb://localhost:27017/test');
const schema = new Schema({
name: String
});
schema.pre('init', obj => {
console.log(obj);
});
const User = connection.model('User', schema);
}
run();
We added the pre init hook with a callback.
Conclusion
We can add middleware for various events that are emitted when we manipulate data with Mongoose.