DocsHub
Transactions

What Are Transactions?

Learn what transactions are, what ACID means, and when you need transactions in MongoDB.

What Are Transactions?

Some operations in your app require multiple writes to the database. For example, enrolling a student in a course in our school system requires three writes:

  1. Add the courseId to the student's courseIds array
  2. Increment totalStudents on the course document
  3. Create a new document in the enrollments collection

What happens if the first write succeeds but your server crashes before the second and third writes? Now the student has the course in their list, but the course enrollment count is wrong and there is no enrollment record. Your data is inconsistent.

A transaction solves this. It groups multiple operations together so they either all succeed or all fail — nothing in between.


The Problem Without Transactions

Start enrollmentfor Ali Hassan in MATH-10 B C D Result — Inconsistent Data❌ Student has course in their list❌ Course count is wrong❌ No enrollment record exists

Without a transaction, a failure halfway through leaves your database in a broken state. Some writes happened, some did not.

Start enrollment transactionfor Ali Hassan in MATH-10 B C D E

With a transaction, if any write fails, MongoDB rolls back all previous writes in that transaction. The database stays consistent — as if none of the writes happened at all.


What is ACID?

ACID is a set of four properties that define what a reliable transaction must guarantee. Every database transaction system — including MongoDB — is measured against these four properties.

Atomicity

All or nothing. Either every operation in the transaction succeeds, or none of them do. There is no partial success.

In our enrollment example — either all three writes succeed together, or all three are rolled back. You will never end up with just one or two of them applied.

Consistency

The database moves from one valid state to another valid state. A transaction never leaves the database in a state that violates your rules.

Before enrollment: student has no course, course has 28 students, no enrollment record. After successful enrollment: student has course, course has 29 students, enrollment record exists. After failed transaction: back to the original state — student has no course, course still has 28 students, no enrollment record.

The database is always consistent — never halfway between states.

Isolation

Transactions do not interfere with each other. While a transaction is in progress, other operations cannot see its partial results.

If two students try to enroll in the same course at the exact same time, each transaction runs as if it is the only one. One completes first, the other completes second. They do not step on each other's data.

Durability

Once committed, the data is permanent. Even if the server crashes immediately after a transaction commits, the data is safely stored and will be there when the server comes back up.


Single Document Operations Are Already Atomic

Before reaching for transactions, it is important to know this — MongoDB already guarantees atomicity for single document operations. A single insertOne, updateOne, or deleteOne is always atomic — it either fully succeeds or fully fails.

// This is already atomic — no transaction needed
// The entire update either happens or it does not
db.students.updateOne(
  { name: "Ali Hassan" },
  {
    $push: { subjects: "Computer Science" },
    $set: { lastUpdated: new Date() },
    $inc: { subjectCount: 1 }
  }
)

Even though this update modifies three things at once — pushing to an array, setting a field, incrementing a counter — it is all one operation on one document. MongoDB handles it atomically without a transaction.

This is one of the big advantages of embedding data. When related data lives in the same document, you never need a transaction to keep it consistent — a single update is always atomic.

Good schema design reduces your need for transactions. If you can update one document instead of three, you get atomicity for free. Reach for transactions only when you truly need to update multiple documents atomically.


When You Need Transactions

You need a transaction when:

You must update multiple documents atomically — like our enrollment example where three documents must all be updated together.

You are moving data between collections — like transferring a fee payment from a pendingPayments collection to a confirmedPayments collection. The delete from one and the insert into the other must happen together.

You need to read, check, then write — sometimes called a read-modify-write pattern. You read a value, make a decision based on it, and write the result. If another operation changes the value between your read and write, you get wrong results. A transaction prevents this.

// Without a transaction — race condition risk
const course = await db.courses.findOne({ code: "MATH-10" });
if (course.totalStudents < course.capacity) {
  // Another enrollment might happen here before this write
  await db.courses.updateOne(
    { code: "MATH-10" },
    { $inc: { totalStudents: 1 } }
  );
}

// With a transaction — safe
// The read and write are isolated together

You are implementing financial operations — fee payments, scholarship disbursements, any operation where money moves. These must always be atomic.


When You Do NOT Need Transactions

Transactions add complexity and have a performance cost. Do not use them when you do not need to.

Single document updates — always atomic, no transaction needed.

Inserts that do not depend on each other — if you are inserting five independent records, each insert is its own atomic operation. No need to wrap them in a transaction unless all five must succeed together.

Operations where eventual consistency is acceptable — if it is okay for the course count to be slightly off for a few milliseconds, you do not need a transaction. Use $inc on its own — it is atomic for a single document.

Read-only operations — reads do not need transactions unless you need a consistent snapshot across multiple collections at the exact same point in time.


Transactions in MongoDB — Requirements

MongoDB supports multi-document transactions but with some requirements:

Replica set or sharded cluster required — transactions only work on a replica set or sharded cluster. They do not work on a standalone MongoDB instance.

For local development, the easiest way is to run MongoDB as a replica set. If you are using MongoDB Atlas, you are already on a replica set — transactions work out of the box.

Start a replica set locally for development:

# In mongosh — initiate a single-node replica set
rs.initiate()

Or use Docker:

docker run -d -p 27017:27017 mongo --replSet rs0
docker exec -it <container> mongosh --eval "rs.initiate()"

MongoDB Atlas runs on a replica set by default. If you are connecting to Atlas, you can use transactions immediately without any extra setup.


Our School System — Where We Need Transactions

Here are the operations in our school system that need transactions:

OperationWhy it needs a transaction
Student enrolls in a courseUpdates student, course, and creates enrollment record — all three must succeed together
Student drops a courseUpdates student, course, and deletes enrollment record
Fee payment processedCreates payment record and updates student fee status
Student transfers gradeUpdates grade on student, updates counts on both old and new grade
Course cancelledRemoves enrollments for all students and updates their records

Quick Summary

ConceptWhat it means
TransactionA group of operations that all succeed or all fail together
AtomicityAll or nothing — no partial success
ConsistencyDatabase always moves between valid states
IsolationTransactions do not see each other's partial results
DurabilityCommitted data survives crashes
Single document opsAlways atomic — no transaction needed
Multi document opsNeed explicit transactions for atomicity

Think of a transaction like a database undo button. Before starting, MongoDB takes note of the current state. If anything goes wrong during the transaction, it presses undo and everything goes back to where it was. If everything succeeds, it commits — and there is no going back.

On this page