Pre middleware in Mongoose is allowed to execute custom logic before specific operations are performed on a model instance. These operations can include saving documents, validating data, or even removing documents. Pre middleware functions in Mongoose are functions that are run before certain operations are executed on a document. They are defined using schema middleware hooks (pre
) and are useful for tasks such as data validation, modification, logging, or triggering additional operations before saving or querying data. Pre middleware can be attached to specific operations like save
, validate
, remove
, and more.
Syntax of Pre Middleware
schema.pre('operation', function(next) {
// Custom logic here
// Access the document using `this`
// Call `next()` to proceed with the operation
});
JavaScriptschema
: The Mongoose schema to which the middleware is attached.'operation'
: The operation (e.g.,save
,validate
,remove
) to attach the middleware to.function(next)
: The middleware function that executes custom logic before the operation.next
: A function that must be called to continue with the next middleware or operation.
Why do we need Pre Middleware in Mongoose?
- Data Validation: It allows you to validate data before saving it to the database, ensuring data integrity and adherence to schema requirements.
- Security Enhancements: You can implement security measures like hashing passwords or sanitizing inputs to prevent vulnerabilities like injection attacks.
- Business Logic: Enables enforcement of business rules and logic at the database level, ensuring consistency in data operations.
- Logging and Auditing: Facilitates logging of operations, providing a traceable history of changes for debugging and auditing purposes.
- Code Modularity: Encourages modular and maintainable code by encapsulating data processing logic separately from application logic.
Example of Pre Middleware
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Define a schema with pre middleware
const userSchema = new Schema({
username: String,
email: String,
password: String
});
// Pre-save middleware to hash passwords before saving
userSchema.pre('save', async function(next) {
if (this.isModified('password')) {
try {
// Hash password logic (using bcrypt for example)
const hashedPassword = await bcrypt.hash(this.password, 10);
this.password = hashedPassword;
} catch (error) {
next(error); // Pass error to next middleware or callback
}
}
next(); // Proceed with saving the document
});
// Create a model
const User = mongoose.model('User', userSchema);
// Usage example
const newUser = new User({
username: 'john_doe',
email: 'john.doe@example.com',
password: 'password123'
});
newUser.save()
.then(user => console.log('User saved successfully:', user))
.catch(err => console.error('Error saving user:', err));
JavaScriptIn this example:
- The
pre('save')
middleware function hashes thepassword
field before saving a newUser
document. - It checks if the
password
field has been modified (this.isModified('password')
) to ensure it only hashes new passwords or modified ones. - “next() is called to continue with saving the document after the password is hashed.
Practical Usage Scenarios
- Password Hashing: Securely hash passwords before storing them in the database.
- Data Validation: Validate fields or perform data transformations before saving documents.
- Logging: Log operations, timestamps, or changes made to documents.
- Preventative Actions: Implement business rules or perform pre-save checks based on application logic.
Conclusion
Pre middleware in Mongoose empowers developers to add sophisticated pre-processing logic before executing critical operations on documents. Whether for securing passwords, validating data, or enforcing business rules, pre middleware enhances data management and maintains data integrity in MongoDB-driven Node.js applications.
Frequently Asked Questions
Yes, pre middleware functions can be async by using async/await
or by returning a Promise
.
Yes, you can attach multiple pre middleware functions to the same operation (save
, validate
, etc.) in Mongoose.
You can skip pre middleware functions by calling this.skip()
within the middleware function based on specific conditions.