MongoDB transactions in Node.js using Mongoose
2022-6-5Mongodb transactions
“In a database management system, a transaction is a single unit of logic or work, sometimes made up of multiple operations.” In simple terms, transactions are used in situations when we need to perform multiple tasks in the database, and we want either all tasks to be successful or none. We expect transactions to have ACID properties.
Transactions in MongoDB
Multi-document transactions in MongoDB enable us to tackle many complex and practical use cases where we want to update multiple documents by maintaining atomicity across all collections in the database.
Using MongoDB Transactions with Node.js
Here, I am using the Mongoose library to do this as it provides a very intuitive interface for transactions.
Let’s take the example of order creation for an e-commerce application. As an order is created, the stock of the corresponding products needs to be updated.
import mongoose from 'mongoose'; import catchAsync from '../utils/catchAsync.js'; import Order from '../models/orderModel.js'; import Product from '../models/productModel.js'; export const createOrder = catchAsync(async (req, res) => { const session = await mongoose.startSession(); // Start the transaction session.startTransaction(); try { // Make the order creation part of the transaction const orderDoc = await Order.create([req.body], { session }); for (const item of orderDoc[0].orderItems) { const product = await Product.findById(item.product).session(session); if (product.stock - item.qty < 0) { throw new Error('Order quantity is more than stock'); } const query = { $inc: { stock: -item.qty }, }; // Make the product update part of the transaction await Product.findByIdAndUpdate(item.product, query, { new: true, runValidators: true, }).session(session); } // If no error, commit the transaction and reflect changes in database await session.commitTransaction(); res.status(201).json({ status: 'success', data: orderDoc, }); } catch (err) { // Abort the transaction if error occurred await session.abortTransaction(); throw err; } finally { session.endSession(); } });
We first create an order document and make it a part of the transaction. Then we update each product in the order with the updated stock values. If an error occurs in any of the product updations, the transaction is aborted. This ensures none of the products are updated and the order is also not created. This is a very typical use case for transactions.
Setting up replica set locally for transactions
If you try to execute the above code locally, it will give an error saying transactions are only available for replica sets. So what does this mean?
A catch with using transactions in MongoDB is that they can only be used with replica sets. If you are using a cloud-hosted database like MongoDB Atlas, it automatically sets up replica sets, so using transactions is not a problem. But in a local environment like your computer, this is not the case. You need to explicitly set up the MongoDB server a replica set for transactions to work.
First shut down the current MongoDB server
Log into the mongo process in the terminal
mongo
Inside the mongo interface, switch to the admin and shut down the server
use admin
db.shutdownServer()
exit
Now start a new mongod process with a replica set
This is for Mac systems. I haven't tried for Linux and Windows systems but I believe, if not the same, it is something very similar.
mongod --port 27017 --replSet rs0 --bind_ip localhost --config /usr/local/etc/mongod.conf --fork
Log into the mongo interface again and initiate the replica set
mongo
rs.initiate()
exit
With this, a replica set is set up locally on your computer and now you can perform MongoDB transactions.
To switch to normal mongodb server without replica set
Shut down the server same as above, then execute the command below.
mongod --config /usr/local/etc/mongod.conf --fork
Conclusion
You can now create Node.js applications using MongoDB transactions and set up a replica set to develop your application locally. You can refer to this github repo I created for the blog. Transactions are a very vast topic in database management, and they have many use cases. I hope, with this, you now have a basic understanding of transactions in MongoDB and can get started with them.