Docs Menu

Docs HomeDevelop ApplicationsMongoDB DriversNode.js Driver

Use the Core API

On this page

  • Example
  • Sample Data
  • Implementation
  • Transaction Results
  • API Documentation

You can perform a transaction to run a series of operations that do not change any data until the entire transaction is committed. This usage example uses the Core API to perform a transaction.

Tip

See also:

To learn more about the performing transactions in the Node.js driver, see the Transactions guide.

The Node.js driver also provides the Convenient Transaction API to perform transactions. To learn more about the Convenient Transaction API, see the Use the Convenient Transaction API usage example.

Consider a situation in which a customer purchases items from your online shop. To record the purchase, your application must update your inventory and the customer's orders. Your application also needs to save the order details.

The following table describes the collections that store purchase data and how a purchase changes the data in each collection.

Collection
Operation
Description of the Change
orders
insert
Inserts a document that describes the order
customers
update or upsert
Appends the _id from the order document to the order history in the customer document
inventory
update
Updates the quantities of items available after a purchase

The code examples use the following sample data in the testdb database:

  • Documents in the customers collection that describe customers and their past orders

  • Documents in the inventory collection that include quantities and descriptions of all items

The following document is in the customers collection:

{ _id: 98765, orders: [] }

The inventory collection contains the following documents:

{ item: "sunblock", item_id: 5432, qty: 85 },
{ item: "beach towel", item_id: 7865, qty: 41 }

You store purchase records in the orders collection of the testdb database. This collection is empty, as there have been no purchases.

The code examples use the cart and payment variables to represent a sample list of items purchased and the order payment details. The following code describes the contents of the cart and payment variables:

const cart = [
{ item: 'sunblock', item_id: 5432, qty: 1, price: 5.19 },
{ item: 'beach towel', item_id: 7865, qty: 2, price: 15.99 }
];
const payment = { customer: 98765, total: 37.17 };

The code example in this section demonstrates how to use the Core API to perform a multi-document transaction in a session. In this example, the transaction makes the changes needed when a customer purchases items from your shop.

This example code performs a transaction through the following actions:

  1. Calls the startSession() method to create a new session

  2. Calls the startTransaction() method with an options parameter to create a new transaction

  3. Performs the following operations within the transaction:

    • Inserts a document to the orders collection that contains information about the purchase and customer

    • Updates the inventory collection if there is sufficient inventory to fulfill the purchase

    • Ends the transaction and throws an exception if there isn't sufficient inventory for any item in the order

    • Adds the ID of the order to the list of past orders for the customer

    • Returns a message acknowledging that the transaction committed successfully with a copy of the purchase record

  4. Calls the commitTransaction() method to commit the transaction if all operations complete successfully

  5. Implements a catch block that contains error-handling logic

  6. Calls the abortTransaction() method to end the transaction

  7. Calls the endSession() method to end the session

async function placeOrder(client, cart, payment) {
const transactionOptions = {
readConcern: { level: 'snapshot' },
writeConcern: { w: 'majority' },
readPreference: 'primary'
};
// Start the session
const session = client.startSession();
try {
// Start the transaction in the session, specifying the transaction options
session.startTransaction(transactionOptions);
const ordersCollection = client.db('testdb').collection('orders');
/* Within the session, insert an order that contains information about the
customer, items purchased, and the total payment */
const orderResult = await ordersCollection.insertOne(
{
customer: payment.customer,
items: cart,
total: payment.total,
},
{ session }
);
const inventoryCollection = client.db('testdb').collection('inventory');
for (const item of order) {
/* Update the inventory for the purchased items. End the
transaction if the quantity of an item in the inventory is
insufficient to complete the purchase. */
const inStock = await inventoryCollection.findOneAndUpdate(
{
item_id: item.item_id,
item_id: { $gte: item.qty }
},
{ $inc: { 'qty': -item.qty }},
{ session }
)
if (inStock === null) {
throw new Error('Insufficient quantity or item ID not found.');
}
}
const customerCollection = client.db('testdb').collection('customers');
// Within the session, add the order details to the "orders" array of the customer document
await customerCollection.updateOne(
{ _id: payment.customer },
{ $push: { orders: orderResult.insertedId }},
{ session }
);
// Commit the transaction to apply all updates performed within it
await session.commitTransaction();
console.log('Transaction successfully committed.');
} catch (error) {
/*
Handle any exceptions thrown during the transaction and end the
transaction. Roll back all the updates performed in the transaction.
*/
if (error instanceof MongoError && error.hasErrorLabel('UnknownTransactionCommitResult')) {
// Add your logic to retry or handle the error
}
else if (error instanceof MongoError && error.hasErrorLabel('TransientTransactionError')) {
// Add your logic to retry or handle the error
} else {
console.log('An error occured in the transaction, performing a data rollback:' + error);
}
await session.abortTransaction();
} finally {
// End the session
await session.endSession();
}
}

This section describes the data changes created by the transaction.

The customers collection contains the customer document with an order _id appended to the orders field:

{
"_id": 98765,
"orders": [
"61dc..."
]
}

The inventory collection contains updated quantities for the items "sunblock" and "beach towel":

[
{
"_id": ...,
"item": "sunblock",
"item_id": 5432,
"qty": 84
},
{
"_id": ...,
"item": "beach towel",
"item_id": 7865,
"qty": 39
}
]

The orders collection contains the order and payment information:

[
{
"_id": "...",
"customer": 98765,
"items": [
{
"item": "sunblock",
"item_id": 5432,
"qty": 1,
"price": 5.19
},
{
"item": "beach towel",
"item_id": 7865,
"qty": 2,
"price": 15.99
}
],
"total": 37.17
}
]

To learn more about any of the methods or types discussed in this usage example, see the following API Documentation:

  • TransactionOptions

  • ClientSession

  • startSession()

  • startTransaction()

  • commitTransaction()

  • abortTransaction()

  • endSession()

← Use the Convenient Transaction API