Use the Core API
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.
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 |
Sample Data
The code examples use the following sample data in the testdb
database:
Documents in the
customers
collection that describe customers and their past ordersDocuments 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 };
Implementation
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:
Calls the
startSession()
method to create a new sessionCalls the
startTransaction()
method with an options parameter to create a new transactionPerforms the following operations within the transaction:
Inserts a document to the
orders
collection that contains information about the purchase and customerUpdates the
inventory
collection if there is sufficient inventory to fulfill the purchaseEnds 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
Calls the
commitTransaction()
method to commit the transaction if all operations complete successfullyImplements a
catch
block that contains error-handling logicCalls the
abortTransaction()
method to end the transactionCalls 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(); } }
Transaction Results
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 } ]
API Documentation
To learn more about any of the methods or types discussed in this usage example, see the following API Documentation: