Skip to content

Firestore Transactions — Atomic Read-Then-Write Operations for Data Consistency

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Firestore Transactions. We cover key concepts, practical examples, and best practices to help you master this topic.

Firestore transactions enable atomic read-then-write operations where the read result is used to determine the write, with automatic retry on concurrent modification and full ACID-like guarantees within a single Transaction.

What You'll Learn

  • Writing transaction functions with read and write operations
  • How Firestore handles transaction retries and conflicts
  • Best practices for transaction design

Why It Matters

Transactions prevent race conditions in scenarios like updating counters, transferring balances, or reserving inventory. Without transactions, concurrent requests cause data corruption. DodaTech uses transactions for license key activation and usage tracking.

sequenceDiagram
    Client->>Firestore: Begin transaction
    Client->>Firestore: Read document balance
    Firestore-->>Client: balance: 100
    Note over Client: Calculate new balance: 90
    Client->>Firestore: Write new balance: 90
    Firestore-->>Client: Commit (may retry if conflict)
    Note over Firestore: If another client wrote balance
between read and write, retry

Code Examples

// Transaction: Transfer money between accounts
import { runTransaction, doc } from 'firebase/firestore';

async function transferMoney(fromId, toId, amount) {
  try {
    await runTransaction(db, async (transaction) => {
      // Read documents inside the transaction
      const fromDoc = await transaction.get(doc(db, 'accounts', fromId));
      const toDoc = await transaction.get(doc(db, 'accounts', toId));

      if (!fromDoc.exists()) {
        throw new Error('Source account does not exist');
      }

      const fromBalance = fromDoc.data().balance;
      if (fromBalance < amount) {
        throw new Error('Insufficient funds');
      }

      // Write updates
      transaction.update(doc(db, 'accounts', fromId), {
        balance: fromBalance - amount
      });
      transaction.update(doc(db, 'accounts', toId), {
        balance: toDoc.data().balance + amount
      });
    });
    console.log('Transfer successful');
  } catch (error) {
    console.error('Transfer failed:', error);
  }
}
// Transaction: Counter increment with retry handling
async function incrementCounter(counterId) {
  const counterRef = doc(db, 'counters', counterId);

  return runTransaction(db, async (transaction) => {
    const counterDoc = await transaction.get(counterRef);

    if (!counterDoc.exists()) {
      transaction.set(counterRef, { count: 1 });
      return 1;
    }

    const newCount = counterDoc.data().count + 1;
    transaction.update(counterRef, { count: newCount });
    return newCount;
  });
}
# Python Firestore transaction
from google.cloud import firestore

db = firestore.Client()

def transfer_funds(from_id, to_id, amount):
    from_ref = db.collection('accounts').document(from_id)
    to_ref = db.collection('accounts').document(to_id)

    @firestore.transactional
    def execute(transaction):
        from_snapshot = transaction.get(from_ref)
        to_snapshot = transaction.get(to_ref)

        if not from_snapshot.exists:
            raise ValueError('Source account does not exist')

        from_balance = from_snapshot.get('balance')
        if from_balance < amount:
            raise ValueError('Insufficient funds')

        transaction.update(from_ref, {'balance': from_balance - amount})
        transaction.update(to_ref, {
            'balance': to_snapshot.get('balance') + amount
        })

    transaction = db.transaction()
    execute(transaction)

Common Mistakes

1. Reading Documents After Writing in the Same Transaction

All reads must occur before writes. Firestore transactions do not support read-after-write.

2. Using Transactions for Single Write Operations

Use simple set/update/delete for operations that do not depend on read results.

3. Not Handling Transaction Failures

Transactions can fail due to contention. Implement retry logic with exponential backoff.

4. Writing Too Many Documents in a Transaction

Transactions are limited to 500 affected documents. Exceeding this causes failure.

5. Performing Non-Firestore Operations Inside Transaction Functions

Transaction functions should only contain Firestore reads and writes. External API calls cause issues.

Practice Questions

  1. What is the difference between a batch write and a transaction?
  2. How does Firestore handle concurrent transaction conflicts?
  3. Can you read a document after writing to it in the same transaction?
  4. What is the maximum number of documents a transaction can affect?
  5. When should you use a transaction vs a batch write?

Answers:

  1. Transactions allow reading before writing; batch writes are write-only.
  2. Firestore retries the transaction function when it detects concurrent modification.
  3. No. All reads must precede all writes in a transaction.
  4. 500 documents.
  5. Use transactions when the write depends on a read value; use batches for independent writes.

Challenge: Build a ticket reservation system with Firestore transactions. Users can reserve tickets, the transaction checks availability and decrements the count atomically. Handle concurrent reservations and timeouts.

FAQ

How many times will Firestore retry a transaction?

Firestore retries transactions up to 5 times by default. If all retries fail, the transaction throws an error.

Do transactions work offline?

No. Transactions require server connectivity because they need to coordinate with the Firestore backend. Use batch writes for offline scenarios.

What isolation level do Firestore transactions provide?

Firestore transactions provide snapshot isolation. The transaction sees a consistent snapshot of the data as of the time the transaction started.

Can a transaction include documents from multiple collections?

Yes. Transactions can read and write documents across any collections within the same Firestore database.

How do transactions affect Firestore read and write costs?

Each read and write inside a transaction counts toward your Firestore usage quotas. There is no additional transaction fee.

Mini Project

Build a simple banking application with Firestore transactions for deposits, withdrawals, and transfers. Include concurrent access testing to demonstrate transaction safety, error handling for insufficient funds, and a transaction log for auditing.

What's Next

Learn about Firestore security rules for access control, then explore security rules functions for advanced validation.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro