Transactions in Mongoose

    If you haven’t already, import mongoose:

    To create a transaction, you first need to create a session using or Mongoose#startSession or .

    1. // Using Mongoose's default connection
    2. const session = await mongoose.startSession();
    3. // Using custom connection
    4. const db = await mongoose.createConnection(mongodbUri, { useUnifiedTopology: true, useNewUrlParser: true });
    5. const session = await db.startSession();
    • Creating a transaction
    • Committing the transaction if it succeeds
    • Aborting the transaction if your operation throws
    • Retrying in the event of a transient transaction error.

    For more information on the ClientSession#withTransaction() function, please see .

    Mongoose’s Connection#transaction() function is a wrapper around withTransaction() that integrates Mongoose change tracking with transactions. For example, the Connection#transaction() function handles resetting a document if you save() that document in a transaction that later fails.

    1. const schema = Schema({ name: String, arr: [String], arr2: [String] });
    2. const Test = db.model('Test', schema);
    3. await Test.createCollection();
    4. let doc = await Test.create({ name: 'foo', arr: ['bar'], arr2: ['foo'] });
    5. doc = await Test.findById(doc);
    6. await db.
    7. transaction(async (session) => {
    8. doc.arr.pull('bar');
    9. doc.arr2.push('bar');
    10. await doc.save({ session });
    11. doc.name = 'baz';
    12. throw new Error('Oops');
    13. catch(err => {
    14. });
    15. const changes = doc.getChanges();
    16. assert.equal(changes.$set.name, 'baz');
    17. assert.deepEqual(changes.$pullAll.arr, ['bar']);
    18. assert.deepEqual(changes.$push.arr2, { $each: ['bar'] });
    19. assert.ok(!changes.$set.arr2);
    20. await doc.save({ session: null });
    21. const newDoc = await Test.findById(doc);
    22. assert.equal(newDoc.name, 'baz');
    23. assert.deepEqual(newDoc.arr, []);
    24. assert.deepEqual(newDoc.arr2, ['foo', 'bar']);

    To get/set the session associated with a given document, use doc.$session().

    The Model.aggregate() function also supports transactions. Mongoose aggregations have a that sets the session option. Below is an example of executing an aggregation within a transaction.

    1. const Event = db.model('Event', new Schema({ createdAt: Date }), 'Event');
    2. const session = await db.startSession();
    3. await session.withTransaction(async () => {
    4. await Event.insertMany([
    5. { createdAt: new Date('2018-06-01') },
    6. { createdAt: new Date('2018-06-02') },
    7. { createdAt: new Date('2017-06-01') },
    8. { createdAt: new Date('2017-05-31') }
    9. ], { session: session });
    10. {
    11. _id: {
    12. month: { $month: '$createdAt' },
    13. year: { $year: '$createdAt' }
    14. },
    15. count: { $sum: 1 }
    16. }
    17. },
    18. { $sort: { count: -1, '_id.year': -1, '_id.month': -1 } }
    19. ]).session(session);
    20. assert.deepEqual(res, [
    21. { _id: { month: 6, year: 2018 }, count: 2 },
    22. { _id: { month: 6, year: 2017 }, count: 1 },
    23. { _id: { month: 5, year: 2017 }, count: 1 }
    24. ]);
    25. });
    26. session.endSession();

    You can also use session.abortTransaction() to abort a transaction:

    1. const session = await Customer.startSession();
    2. session.startTransaction();
    3. await Customer.create([{ name: 'Test' }], { session: session });
    4. await Customer.create([{ name: 'Test2' }], { session: session });
    5. await session.abortTransaction();
    6. const count = await Customer.countDocuments();
    7. assert.strictEqual(count, 0);