A single updateOne() is always atomic on its own — it either fully applies or doesn't. A transaction extends that same all-or-nothing guarantee across several operations, possibly touching several documents or collections, the same concept as the earlier SQL course's transactions lesson.
Moving money from one account to another needs two writes — subtract from one document, add to another. Without a transaction, a crash between the two writes leaves the data in a genuinely broken, inconsistent state.
const session = db.getMongo().startSession()
try {
session.startTransaction()
const accounts = session.getDatabase('bank').accounts
accounts.updateOne({ _id: 'A' }, { $inc: { balance: -100 } })
accounts.updateOne({ _id: 'B' }, { $inc: { balance: 100 } })
session.commitTransaction()
} catch (error) {
session.abortTransaction()
throw error
} finally {
session.endSession()
}Both updates commit together, or — if anything fails in between — neither one does, exactly like the earlier SQL transactions lesson's BEGIN/COMMIT/ROLLBACK.
MongoDB replicates data across multiple servers for durability — write concern and read concern tune how much of that replication a write or read waits for before being considered done.
| Write concern | Waits for |
|---|---|
| w: 1 (default) | Acknowledgment from the primary server only |
| w: 'majority' | Acknowledgment from a majority of replica servers — safer, slightly slower |
| w: 0 | No acknowledgment at all — fastest, least safe |
db.accounts.updateOne(
{ _id: 'A' },
{ $inc: { balance: -100 } },
{ writeConcern: { w: 'majority' } }
)Transactions and write concern are separate guarantees
A transaction, by itself, only guarantees the writes inside it succeed or fail together — it says nothing about how durably they're replicated. For genuinely critical operations (like the money transfer above), pairing a transaction with a majority write concern is what most real applications actually use.
Not a default choice
A transaction that touches many documents or runs for a long time can hurt performance across the whole database — reach for one specifically when an operation genuinely needs the all-or-nothing guarantee, not as a default habit for every multi-step write.