Docs Menu

Docs HomeDevelop ApplicationsMongoDB DriversNode.js Driver

Transactions

On this page

  • Overview
  • Transaction APIs
  • Core API
  • Convenient Transaction API
  • Transaction Options
  • Transaction Errors

In this guide, you can learn how to use the Node.js driver to perform transactions. Transactions allow you to run a series of operations that do not change any data until the entire transaction is committed. If any operation in the transaction fails, the driver ends the transaction and discards all data changes before they ever become visible. This feature is called atomicity.

Since all write operations on a single document in MongoDB are atomic, you might want to use transactions to make an atomic change that modifies multiple documents. This situation requires a multi-document transaction. Multi-document transactions are ACID compliant because MongoDB guarantees that the data involved in your transaction operations remains consistent, even if the driver encounters unexpected errors.

To learn more about ACID compliance and transactions, see our article on ACID transactions.

Note

To execute a multi-document transaction, you must be connected to a deployment running MongoDB Server version 4.0 or later.

For a detailed list of limitations, see the Transactions and Operations section in the Server manual.

In MongoDB, multi-document transactions run within a client session. A client session is a grouping of related read or write operations that you want to execute sequentially. We recommend you reuse your client for multiple sessions and transactions instead of instantiating a new client each time.

When combined with majority read and write concerns, the driver guarantees causal consistency between the operations. To learn more, see Client Sessions and Causal Consistency Guarantees in the Server manual.

Learn more about how to use the driver to perform multi-document transactions in the following sections of this guide:

The driver provides two APIs for performing transactions, the Core API and the Convenient Transaction API.

The Core API is a framework that enables you to create, commit, and end transactions. When using this API, you must explicitly perform the following actions:

  • Create, commit, and end the transaction.

  • Create and end the session in which you run the transaction.

  • Implement error-handling logic.

The Convenient Transaction API is a framework that enables you to perform transactions without being responsible for committing or ending them. This API automatically incorporates error-handling logic to retry operations when the server raises certain error types. To learn more about this behavior, see the Transaction Errors section of this guide.

Important

When you connect to MongoDB Server version 4.2 or earlier, you can perform write operations in a transaction only on collections that already exist. When you connect to MongoDB Server version 4.4 and later, the server automatically creates collections as necessary when you perform write operations in a transaction. To learn more about this behavior, see Create Collections and Indexes in a Transaction in the Server manual.

The Core API provides the following methods to implement transactions:

You must perform the following steps when using this API:

  • Pass the session instance to each operation that you want to run in that session.

  • Implement a catch block in which you identify server transaction errors and implement error-handling logic.

The following code demonstrates how to perform a transaction by using the Core API:

async function coreTest(client) {
const session = client.startSession();
try {
session.startTransaction();
const savingsColl = client.db("bank").collection("savings_accounts");
await savingsColl.findOneAndUpdate(
{account_id: "9876"},
{$inc: {amount: -100 }},
{ session });
const checkingColl = client.db("bank").collection("checking_accounts");
await checkingColl.findOneAndUpdate(
{account_id: "9876"},
{$inc: {amount: 100 }},
{ session });
// ... perform other operations
await session.commitTransaction();
console.log("Transaction committed.");
} catch (error) {
console.log("An error occurred during the transaction:" + error);
await session.abortTransaction();
} finally {
await session.endSession();
}
}

Important

Use a Session with the Client That Started It

The driver throws an error if you provide a session from one MongoClient instance to a different client instance.

For example, the following code generates an MongoInvalidArgumentError error because it creates a ClientSession instance from the client1 client, but provides this session to the client2 client for a write operation:

const session = client1.startSession();
client2.db('myDB').collection('myColl').insertOne({ name: 'Jane Eyre' }, { session });

To see a fully runnable example that uses this API, see the Use the Core API usage example.

The Convenient Transaction API provides the following methods to implement transactions:

  • withSession(): Runs the callback passed to it within a session. The API handles the creation and termination of the session automatically.

  • withTransaction(): Runs the callback passed to it within a transaction and calls the commitTransaction() method when the callback returns.

These methods return the value that the callback returns. For example, if a callback you pass to the withTransaction() method returns the document { hello: "world" }, then the withTransaction() method also returns that document.

When you use the Convenient Transaction API, you can propagate return values from the callback as the return values of the withTransaction() and withSession() methods to work with them elsewhere in your code.

You must perform the following steps when using this API:

  • Pass the session instance to each operation that you want to run in that session.

  • Implement the async await syntax for each operation in the session.

  • Avoid parallelism, such as calling the Promise.all() method. Using sessions in parallel usually leads to server errors.

The following code demonstrates how to perform a transaction by using the Convenient Transaction API:

async function convTest(client) {
let txnRes = await client.withSession(async (session) =>
session.withTransaction(async (session) => {
const savingsColl = client.db("bank").collection("savings_accounts");
await savingsColl.findOneAndUpdate(
{account_id: "9876"},
{$inc: {amount: -100 }},
{ session });
const checkingColl = client.db("bank").collection("checking_accounts");
await checkingColl.findOneAndUpdate(
{account_id: "9876"},
{$inc: {amount: 100 }},
{ session });
// ... perform other operations
return "Transaction committed.";
}, null)
);
console.log(txnRes);
}

To see a fully runnable example that uses this API, see the Use the Convenient Transaction API usage example.

You can pass a TransactionOptions instance to the startTransaction() and withTransaction() methods to configure how the driver performs a transaction. When you specify an option, it overrides the value of the option that you might have set on your MongoClient instance.

The following table includes options that you can specify in a TransactionOptions instance:

Setting
Description
readConcern
Specifies read operation consistency of the replica set.
To learn more, see Read Concern in the Server manual.
writeConcern
Specifies the write operation level of acknowledgment required from a replica set.
To learn more, see Write Concern in the Server manual.
readPreference
Specifies how to route read operations to members of a replica set.
To learn more, see Read Preference in the Server manual.
maxCommitTimeMS
Specifies the length of time that a commit action on a transaction can run, in milliseconds.

For a full list of options, see the API documentation for TransactionOptions.

Note

The transaction inherits settings from your MongoClient instance unless you specify them in your transaction options.

The following code shows how to define and pass transaction options to the startTransaction() method:

const txnOpts = {
readPreference: 'primary',
readConcern: { level: 'local' },
writeConcern: { w: 'majority' },
maxCommitTimeMS: 1000
};
session.startTransaction(txnOpts);

If you are using the Core API to perform a transaction, you must incorporate error-handling logic into your application for the following errors:

  • TransientTransactionError: Raised if a write operation errors before the driver commits the transaction. To learn more about this error, see the TransientTransactionError description on the Driver API page in the Server manual.

  • UnknownTransactionCommitResult: Raised if the commit operation encounters an error. To learn more about this error, see the UnknownTransactionCommitResult description on the Driver API page in the Server manual.

The Convenient Transaction API incorporates retry logic for these error types, so the driver retries the transaction until there is a successful commit.

← Aggregation