Open In App

Transactions in Mongoose

Last Updated : 16 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

In modern web applications, maintaining data integrity across multiple database operations is crucial. Transactions in Mongoose allow developers to execute multiple database operations within a single transaction, ensuring that all operations are successful, or none are executed.

This guarantees consistency and prevents partial updates, a vital aspect of robust application development. This article will explain in depth how Mongoose supports transactions, covering everything from basic usage to advanced transaction management techniques.

What are Mongoose Transactions?

Mongoose transactions are designed to manage complex operations that need to be executed atomically. In simple terms, they ensure that multiple operations on your MongoDB database either all succeed or all fail, helping maintain the consistency and integrity of your data.

Step 1: Setting Up Mongoose in Your Node.js Application

Before diving into transactions, let's first set up Mongoose in your Node.js application. Make sure you have installed Mongoose and have it connected to your MongoDB database:

import mongoose from 'mongoose';

Step 2: Creating a Session for Transactions

Transactions in Mongoose are managed through sessions. You can create a session using either the default Mongoose connection or a custom connection:

// Using the default Mongoose connection
const session = await mongoose.startSession();
// Using a custom connection
const db = await mongoose.createConnection(mongodbUri).asPromise();
const session = await db.startSession();

Step 3: Performing Operations within a Transaction

Once you have a session, you can perform transactions using the session.withTransaction() helper or Mongoose's Connection#transaction() function. These functions simplify the transaction management, ensuring that all operations within the transaction are executed correctly.

let session = null;
return Customer.createCollection()
.then(() => Customer.startSession())
.then(_session => {
session = _session;
return session.withTransaction(() => {
return Customer.create([{ name: 'Test' }], { session: session });
});
})
.then(() => Customer.countDocuments())
.then(count => console.log(`Total customers: ${count}`))
.then(() => session.endSession());

Transactions with Mongoose Documents

Mongoose makes it easy to work with transactions when dealing with documents. If you retrieve a Mongoose document using a session, the document will automatically use that session for save operations. You can use doc.$session() to get or set the session associated with a specific document.

Example: In this example, we work with Mongoose documents within transactions:

  • We create a User model and initiate a session and a user document with the name 'john'.
  • We start a transaction and use the session for queries, like finding the user with the name 'john'.
  • We modify the user's name to 'smith' and save the document and confirm that the updated document exists.
  • We commit the transaction and end the session.
JavaScript
const UserModel = 
    db.model('User', new Schema({ name: String }));

let userSession = null;
return UserModel.createCollection()
    .then(() => db.startSession())
    .then(session => {
        userSession = session;
        const userToCreate = 
            UserModel.create({ name: 'john' });
        return userToCreate;
    })
    .then(() => {
        userSession.startTransaction();
        return UserModel
            .findOne({ name: 'john' }).session(userSession);
    })
    .then(user => {
        assert.ok(user.$session());
        user.name = 'smith';
        return user.save();
    })
    .then(() => UserModel.findOne({ name: 'smith' }))
    .then(result => {
        assert.ok(result);
        userSession.commitTransaction();
        userSession.endSession();
    });

Transactions with the Aggregation Framework

Mongoose's Model.aggregate() function also supports transactions. You can use the session() helper to set the session option for aggregations within a transaction. This is useful when you need to perform complex aggregations as part of a transaction.

Example: In this example, we use transactions with the Mongoose Aggregation Framework:

  • We create an Event model and initiate a session.
  • We start a transaction and use the session to insert multiple documents into the Event collection.
  • We perform an aggregation operation using Event.aggregate() within the transaction.
  • We commit the transaction and end the session.
JavaScript
const EventModel = db.model('Event',
    new Schema({ createdAt: Date }), 'Event');

let eventSession = null;
let insertManyResult = null;
let aggregationResult = null;

return EventModel.createCollection()
    .then(() => db.startSession())
    .then(session => {
        eventSession = session;
        eventSession.startTransaction();

        const documentsToInsert = [
            { createdAt: new Date('2018-06-01') },
            { createdAt: new Date('2018-06-02') },
            // ... other documents ...
        ];

        return EventModel.insertMany(documentsToInsert,
            { session: eventSession })
            .then(result => {
                insertManyResult = result;
            });
    })
    .then(() => EventModel.aggregate([
        // Your aggregation pipeline here
    ]).session(eventSession))
    .then(result => {
        aggregationResult = result;
        return eventSession.commitTransaction();
    })
    .then(() => eventSession.endSession())

Advanced Usage and Manual Transaction Control

For advanced users who require more control over when to commit or abort transactions, you can manually start a transaction using session.startTransaction(). This allows you to execute operations within and outside of the transaction as needed. You can also use session.abortTransaction() to manually abort a transaction if needed.

Example: This example demonstrates manual transaction control:

  • We create a Customer model and initiate a session.
  • We start a transaction and use the session for operations like creating a customer.
  • We verify that the customer is not found before committing the transaction.
  • We use the session for further queries, confirming the customer's existence.
  • We commit the transaction and verify that the customer is still present after the commit. Finally, we end the session.
JavaScript
const CustomerModel = db.model('Customer',
    new Schema({ name: String }));

let customerSession = null;
let createCustomerResult = null;
let findCustomerResult = null;

return CustomerModel.createCollection()
    .then(() => db.startSession())
    .then(session => {
        customerSession = session;
        customerSession.startTransaction();

        const customerToCreate = [{ name: 'Test' }];

        return CustomerModel.create(customerToCreate,
            { session: customerSession })
            .then(result => {
                createCustomerResult = result;
            });
    })
    .then(() => CustomerModel.findOne({ name: 'Test' }))
    .then(result => {
        findCustomerResult = result;
        assert.ok(!result);
    })
    .then(() => CustomerModel.findOne({ name: 'Test' })
        .session(customerSession))
    .then(doc => {
        assert.ok(doc);
    })
    .then(() => customerSession.commitTransaction())
    .then(() => CustomerModel.findOne({ name: 'Test' }))
    .then(result => {
        assert.ok(result);
    })
    .then(() => customerSession.endSession())
    .then(() => {
        // Now you can access 
        // createCustomerResult and findCustomerResult
        console.log('Create Customer Result:',
            createCustomerResult);
        console.log('Find Customer Result:',
            findCustomerResult);
    });

Why Use Transactions in Mongoose?

Transactions are crucial for ensuring that your MongoDB database maintains data consistency. By using transactions, you can:

  1. Guarantee atomicity: Ensure multiple database operations are treated as a single unit of work.
  2. Maintain data integrity: Even if an error occurs, no partial changes will be committed to the database.
  3. Enable complex workflows: Execute multiple related operations, such as updates and inserts, in a consistent manner.

Conclusion

Transactions in Mongoose provide a powerful mechanism for ensuring data consistency and integrity in your MongoDB-based Node.js applications. By grouping multiple operations into atomic units, we can guarantee that your data remains in a reliable state, even in the face of errors or failures. Whether you're working with documents, aggregations, or complex operations, Mongoose's support for transactions makes it easier than ever to build robust and reliable applications on top of MongoDB.


Next Article

Similar Reads