DEV Community

Cover image for Mastering Transactions in MongoDB: A Beginner’s Guide
Rutvik Makvana
Rutvik Makvana

Posted on

Mastering Transactions in MongoDB: A Beginner’s Guide

Understanding Transactions in MongoDB

Transactions are an essential concept in database systems. They ensure that a series of operations either succeed entirely or fail completely—following the all-or-nothing rule. If something goes wrong during a transaction, all changes made are rolled back, leaving the database unchanged.


Why Are Transactions Important?

Transactions play a critical role in maintaining data consistency, especially in scenarios like:

  1. E-commerce: Deducting stock and processing payment should either both succeed or fail to avoid mismatches.
  2. Banking: Money transfers must ensure that the sender is debited only when the receiver is credited.
  3. Event Booking: Both booking confirmation and payment processing need to happen together.

Without transactions, partial updates can lead to inconsistent or corrupted data.


How Do Transactions Work in MongoDB?

MongoDB ensures data integrity by adhering to the ACID principles:

  1. Atomicity: All operations in a transaction either succeed or fail as one.
  2. Consistency: The database state remains valid after a transaction.
  3. Isolation: Transactions don’t interfere with one another.
  4. Durability: Committed changes are permanently saved, even in the event of a failure.

Transaction Lifecycle in MongoDB Using Mongoose

Let’s break down the transaction process step-by-step:

  1. Start a Session: Begin a session to start the transaction.
  2. Perform Operations: Execute all necessary operations as part of the transaction.
  3. Commit: If everything is successful, commit the transaction to save the changes.
  4. Rollback (Abort): If any operation fails, abort the transaction to revert all changes.

Here’s how you can implement this in a Node.js application using Mongoose.


Step 1: Define Schemas

Product Schema

import mongoose from "mongoose";

const productSchema = new mongoose.Schema({
  name: { type: String },
  price: { type: Number },
  stock: { type: Number },
}, { timestamps: true });

const Product = mongoose.model("Product", productSchema);

export default Product;

Enter fullscreen mode Exit fullscreen mode

Order Schema

import mongoose from "mongoose";

const orderSchema = new mongoose.Schema({
  userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
  productId: { type: mongoose.Schema.Types.ObjectId, ref: "Product" },
  totalPrice: { type: Number },
}, { timestamps: true });

const Order = mongoose.model("Order", orderSchema);

export default Order;
Enter fullscreen mode Exit fullscreen mode

Step 2: Implement Transaction Logic

import mongoose from "mongoose";
import Product from "./models/Product";
import Order from "./models/Order";

const createOrder = async (userId, productId, quantity) => {
  const session = await mongoose.startSession();
  session.startTransaction();

  try {
    // Fetch the product
    const product = await Product.findById(productId).session(session);
    if (!product) throw new Error("Product not found");

    // Check stock availability
    if (product.stock < quantity) throw new Error("Insufficient stock");

    // Calculate total price
    const totalPrice = product.price * quantity;

    // Create the order
    const order = new Order({ userId, productId, totalPrice });
    await order.save({ session });

    // Update product stock
    product.stock -= quantity;
    await product.save({ session });

    // Commit transaction
    await session.commitTransaction();
    session.endSession();

    console.log("Order created successfully:", order);
    return order;
  } catch (error) {
    // Rollback transaction
    await session.abortTransaction();
    session.endSession();
    console.error("Transaction failed:", error.message);
    throw error;
  }
};

Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  1. Session Handling: The session is started with mongoose.startSession() and passed to both the findById, save, and update operations to ensure they are all part of the same transaction.
  2. Error Handling: If any part of the transaction fails (e.g., insufficient stock or product not found), the entire transaction will be rolled back, and no changes will be saved to the database.
  3. Atomic Operations: By using transactions, you ensure that both the creation of the order and the stock update happen atomically. This prevents partial updates, where the order might be created without reducing stock, or vice versa.
  4. MongoDB Replica Set Requirement: Transactions require MongoDB to be running as a replica set, even in single-node deployments, so ensure your MongoDB is set up correctly for transactions.

Let’s Connect!

🚀 Enjoyed learning about transactions or exploring backend development?
📚 I share similar blogs, tutorials, and insights regularly.

👨‍💻 Follow My GitHub!

✨ Explore my open-source projects and dive into real-world examples.
🔗 Check out my GitHub :- [https://github.com/RutvikMakvana4]

💼 Connect on LinkedIn!

🤝 Expand your network and stay updated with modern web development trends, career advice, and project highlights.
🔗 Connect with me on LinkedIn :- [https://www.linkedin.com/in/rutvik-makvana-b619b3214/]


Follow My Journey!

Stay tuned for more blogs and insights on backend development, MongoDB, and Node.js.

If you found this helpful:

  1. Leave a Like or Comment! Share your thoughts and questions.
  2. Share it! Help others discover tips and tricks in backend development.

Let’s grow and learn together! Happy coding!🌟

Top comments (0)