Home » Pre Middleware in Mongoose

Pre Middleware in Mongoose

Pre Middleware in Mongoose

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
});
JavaScript
  • schema: 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?

  1. Data Validation: It allows you to validate data before saving it to the database, ensuring data integrity and adherence to schema requirements.
  2. Security Enhancements: You can implement security measures like hashing passwords or sanitizing inputs to prevent vulnerabilities like injection attacks.
  3. Business Logic: Enables enforcement of business rules and logic at the database level, ensuring consistency in data operations.
  4. Logging and Auditing: Facilitates logging of operations, providing a traceable history of changes for debugging and auditing purposes.
  5. 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));
JavaScript

In this example:

  • The pre('save') middleware function hashes the password field before saving a new User 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

  1. Password Hashing: Securely hash passwords before storing them in the database.
  2. Data Validation: Validate fields or perform data transformations before saving documents.
  3. Logging: Log operations, timestamps, or changes made to documents.
  4. 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

1. Can pre middleware functions be async in Mongoose?

Yes, pre middleware functions can be async by using async/await or by returning a Promise.

2. Can I attach multiple pre middleware functions to the same operation?

Yes, you can attach multiple pre middleware functions to the same operation (save, validate, etc.) in Mongoose.

3. How can I skip pre middleware functions under certain conditions?

You can skip pre middleware functions by calling this.skip() within the middleware function based on specific conditions.