Transactions in Mongoose

Transactions are new in MongoDB 4.0 and Mongoose 5.2.0. Transactions let you execute multiple operations in isolation and potentially undo all the operations if one of them fails. This guide will get you started using transactions with Mongoose.

Your First Transaction

MongoDB currently only supports transactions on replica sets, not standalone servers. To run a local replica set for development on OSX or Linux, use npm to install run-rs globally and run run-rs --version 4.0.0. Run-rs will download MongoDB 4.0.0 for you.

To use transactions with Mongoose, you should use Mongoose >= 5.2.0. To check your current version of Mongoose, run npm list | grep "mongoose" or check the mongoose.version property.

Transactions are built on MongoDB sessions. To start a transaction, you first need to call startSession() and then call the session's startTransaction() function. To execute an operation in a transaction, you need to pass the session as an option.

const Customer = db.model('Customer', new Schema({ name: String }));

let session = null;
return db.createCollection('customers').
  then(() => db.startSession()).
  then(_session => {
    session = _session;
    // Start a transaction
    session.startTransaction();
    // This `create()` is part of the transaction because of the `session`
    // option.
    return Customer.create([{ name: 'Test' }], { session: session });
  }).
  // Transactions execute in isolation, so unless you pass a `session`
  // to `findOne()` you won't see the document until the transaction
  // is committed.
  then(() => Customer.findOne({ name: 'Test' })).
  then(doc => assert.ok(!doc)).
  // This `findOne()` will return the doc, because passing the `session`
  // means this `findOne()` will run as part of the transaction.
  then(() => Customer.findOne({ name: 'Test' }).session(session)).
  then(doc => assert.ok(doc)).
  // Once the transaction is committed, the write operation becomes
  // visible outside of the transaction.
  then(() => session.commitTransaction()).
  then(() => Customer.findOne({ name: 'Test' })).
  then(doc => assert.ok(doc));

With Mongoose Documents and save()

If you get a Mongoose document from findOne() or find() using a session, the document will keep a reference to the session and use that session for save().

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

const User = db.model('User', new Schema({ name: String }));

let session = null;
return db.createCollection('users').
  then(() => db.startSession()).
  then(_session => {
    session = _session;
    return User.create({ name: 'foo' });
  }).
  then(() => {
    session.startTransaction();
    return User.findOne({ name: 'foo' }).session(session);
  }).
  then(user => {
    // Getter/setter for the session associated with this document.
    assert.ok(user.$session());
    user.name = 'bar';
    // By default, `save()` uses the associated session
    return user.save();
  }).
  then(() => User.findOne({ name: 'bar' })).
  // Won't find the doc because `save()` is part of an uncommitted transaction
  then(doc => assert.ok(!doc)).
  then(() => {
    session.commitTransaction();
    return User.findOne({ name: 'bar' });
  }).
  then(doc => assert.ok(doc));
Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#