Middleware trong Mongoose
Trong bài viết này chúng ta sẽ cùng nhau đi tìm hiểu về Middleware trong Mongoose, trong các bài trước các bạn cũng thấy mình nhắc đến middleware rất nhiều như : validation middleware, save middleware,...bởi vậy bài viết này sẽ tập chung tìm hiểu về các middleware được xây dựng trong Mongoose.
Middleware (pre hay post hooks) là một function mà nó được gọi bất cứ khi nào mà một hàm bất đồng bộ được thực thi. Middleware được chỉ định trong moojy Schema.
1. Một số loại Middleware trong Mongoose
Mongoose có 4 loại middleware: document middleware, model middleware, aggregate middleware, và query middleware.
Trong đó document middleware hỗ trợ việc làm việc với các hàm document. Trong một document middleware chúng ta có các query như:
Bài viết này được đăng tại [free tuts .net]
- validate
- save
- remove
- updateOne
- deleteOne
- init (init hooks là một hàm đồng bộ)
Query middleware hỗ trợ việc theo dõi Model và hàm Query. Trong query middleware chúng ta có một vài query như:
- count
- deleteMany
- deleteOne
- find
- findOne
- findOneAndDelete
- findOneAndRemove
- findOneAndUpdate
- remove
- update
- updateOne
- updateMany
Aggregate middleware hỗ trợ MyModel.aggregate()
. Aggregate middleware được thực thi khi bạn gọi hàm exec()
trong một aggregate object. Trong Aggregate middleware chúng ta có query như :
- aggregate
Model middleware hỗ trợ việc theo dõi các model function, Trong Model middleware chúng ta có query như
- insertMany
Lưu ý : Tất cả các loại middleware đề hỗ trợ pre và post hooks.
2. Pre middleware function
Pre middleware được thực hiện sau mỗi lần được gọi.
var schema = new Schema(..); schema.pre('save', function(next) { // do somethings next(); });
Trong mongoose 5.x, thay vì sử dụng next()
bạn có thể sử return về một promise. Trong ví dụ bên duới mình sẽ sử dụng async/await :
schema.pre('save', function() { return doStuff(). then(() => doMoreStuff()); }); // Or, in Node.js >= 7.6.0: schema.pre('save', async function() { await doStuff(); await doMoreStuff(); });
Khi bạn gọi next()
nó sẽ được chuyển sang ngay sang middlware tiếp theo mà vẫn chạy câu lệnh ở bên dưới middleware hiện tại. Để chuyển ngay sang middelware tiếp theo mà không thực thi câu lệnh bên dưới thì chúng ta sử dụng return next().
var schema = new Schema(..); schema.pre('save', function(next) { if (foo()) { console.log('calling next!'); // `return next();` sẽ ngăn câu lệnh tiếp theo thực thi /*return*/ next(); } // Nếu bỏ return thì câu lệnh này vẫn sẽ chạy. console.log('after next'); });
Middleware được sử dụng rất nhiều trong việc xử lý các tác vụ có tính logic cao:
- Những validation phức tạp.
- Xóa một doucment phụ thuộc nào đó (ví dụ như xóa một người dùng khỏi blog của bạn đồng thơi sẽ xóa comment, ảnh,vv...)
- Các hàm bất đồng bộ thông thường.
- Các tác vụ bất đông bộ chứa nhiều hành động.
Error trong Pre Hooks
Nếu bất cứ pre hook nào xảy ra lỗi, mongoose sẽ không thực thi các subsequent middleware hoặc hooked function. Mà nó sẽ gán vào tham số error
trong hàm callback hoặc return reject
trong promise. Ở đây chúng ta có một vài cách để bắt error
trong pre hooks:
schema.pre('save', function(next) { const err = new Error('something went wrong'); //Gọi next() mà truyền vào một tham số err next(err); }); schema.pre('save', function() { // Return về một project rồi reject lỗi return new Promise((resolve, reject) => { reject(new Error('something went wrong')); }); }); schema.pre('save', function() { // Throw error như là một tác vụ đồng bộ throw new Error('something went wrong'); }); schema.pre('save', async function() { await Promise.resolve(); // Return error promise (async/await) throw new Error('something went wrong'); }); // Hiển thi ra lỗi myDoc.save(function(err) { console.log(err.message); // something went wrong });
4. Post middleware
Post middleware được thực thi khi sau khi tất các các hook method hay pre middleware được hoàn thành.
schema.post('init', function(doc) { console.log('%s đã được bắt đầu', doc._id); }); schema.post('validate', function(doc) { console.log('%s đã được validate (nhưng chưa luư )', doc._id); }); schema.post('save', function(doc) { console.log('%s Đã được lưu', doc._id); }); schema.post('remove', function(doc) { console.log('%s Đã được remove', doc._id); });
Asynchronous Post Hooks
Nếu bạn gọi 2 posts hook giống nhau, mongoose sẽ thực hiện lần lượt các middleware thứ 1 rồi đến thứ 2.
// Thực hiện middleware này đầu tiên schema.post('save', function(doc, next) { setTimeout(function() { console.log('post1'); //Gọi next() next(); }, 10); }); // Sau đó đợi middleware trên gọi next() thì middleware này mới chạy schema.post('save', function(doc, next) { console.log('post2'); next(); });
5. Một vài lưu ý
Dưới đây là một vài lưu ý trong khi làm việc với middleware.
Chỉ định middleware trước Model
Trong trường hợp bạn gọi pre()
hay post()
sau khi khởi tạo model thường thì sẽ không hoạt động trong Mongoose. Ví dụ bên dưới, pre('save')
sẽ không được thực thi.
const schema = new mongoose.Schema({ name: String }); // Compile model từ schema const User = mongoose.model('User', schema); //Lúc này mongoose sẽ không gọi pre('save') vì nó được chỉ định sau //khi complie schema.pre('save', () => console.log('Hello from pre save')); new User({ name: 'test' }).save();
Muốn nó chạy thì bạn phải đặt nó lên trên trước khi complie Model nhé.
const schema = new mongoose.Schema({ name: String }); schema.pre('save', () => console.log('Hello from pre save')); // Compile model từ schema const User = mongoose.model('User', schema); new User({ name: 'test' }).save();
Save/Validate Hooks
Thứ tự thực thi của các pre/post hook
sẽ theo thứ thự như sau:
schema.pre('validate', function() { console.log('1'); }); schema.post('validate', function() { console.log('2'); }); schema.pre('save', function() { console.log('3'); }); schema.post('save', function() { console.log('4'); });
Naming Conflicts
Mongoose có 2 query và document hooks dành cho remove()
schema.pre('remove', function() { console.log('Removing!'); }); // In ra "Removing!" doc.remove(); // Không in ra "Removing!". // Query middleware cho 'remove' không được thực thi theo các thông thường Model.remove();
Bạn có thể sử dụng bằng cách thêm tùy chọn vào cho nó.
// document middleware schema.pre('remove', { document: true }, function() { console.log('Removing doc!'); }); // query middleware. schema.pre('remove', { query: true }, function() { console.log('Removing!'); });
6. Error Handling Middleware
Middleware thông thường bị dừng lại khi middleware đầu tiên gọi next()
bị lỗi. Middleware cũng có thể được dùng để bắt các lỗi được xảy ra. Error handling middleware được xác đinh với các tham số nhưng tham số đầu tiên là error chứa các lỗi của middleware.
Ở bên dưới mình có một ví dụ :
var schema = new Schema({ name: { type: String, // Will trigger a MongoError with code 11000 when // you save a duplicate unique: true } }); // Handler **must** take 3 parameters: the error that occurred, the document // in question, and the `next()` function schema.post('save', function(error, doc, next) { if (error.name === 'MongoError' && error.code === 11000) { next(new Error('There was a duplicate key error')); } else { next(); } }); // Will trigger the `post('save')` error handler Person.create([{ name: 'Axl Rose' }, { name: 'Axl Rose' }]);
Trên đây là những kiến thức cơ bản về Middleware trong Mongoose. Mong bài viết này có thể giúp ích cho bạn cho việc lập trình với NodeJS, cảm ơn bạn đã quan tâm bài viết này.