Using Firestore Batched Writes for Atomic Multi‑Document Updates
Learn how Firestore batched writes let you group up to 500 create, update, or delete operations into a single atomic request, with a Node.js example, limits, and common pitfalls.
15 Oct 2025, 02:08 UTC

Useful answer
Firestore batched writes let you group up to 500 create, update, or delete operations into a single request that is either fully applied or not applied at all. This gives you atomicity across multiple documents without the retry loop required by transactions.
How batched writes work – a worked Node.js example
A batch collects write operations locally; when you call commit() the Firestore backend receives one commit request containing all the operations. If any operation would violate a rule, the entire request is rejected and none of the writes are persisted.
Where to run: any Node.js environment that can initialize the Firebase Admin SDK (Cloud Functions, a backend server, or a local machine with the Firestore emulator). Required permissions: the service account used by the SDK must have datastore.entities.create, datastore.entities.update, and datastore.entities.delete on the target database. Risks: if the batch exceeds the write count or payload size limits the entire request fails and no writes are applied, so validate before committing.
// Initialize Admin SDK (ensure you have a service account with proper roles)
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.applicationDefault() });
const db = admin.firestore();
// Start a new batch
const batch = db.batch();
// Document references
const aliceRef = db.collection('users').doc('alice');
const statsRef = db.collection('stats').doc('daily');
// Add operations
batch.set(aliceRef, {
lastLogin: admin.firestore.FieldValue.serverTimestamp()
});
batch.update(statsRef, {
logins: admin.firestore.FieldValue.increment(1)
});
// Commit atomically
await batch.commit();
console.log('Batch committed successfully');
Limits and constraints
- Maximum of 500 write operations per batch (creates, updates, or deletes each count as one).
- Total request payload must be under 10 MiB.
- You cannot read documents inside a batch; if you need to read‑modify‑write logic use a transaction instead.
- Batched writes do not guarantee ordering relative to other concurrent batches; rely on eventual consistency for cross‑batch dependencies.
- Each document write also counts toward the per‑document size limit (1 MiB).
Common mistakes and how to avoid them
- Exceeding the 500‑write limit – Firestore returns a
FailedPreconditionerror. Split the work into multiple batches or use a transaction when you need fewer than 500 writes with reads. - Forgetting to
awaitthe commit – the function resolves before the writes are sent, leading to silent failures. Alwaysawait batch.commit()or handle the returned promise. - Mixing batch with transaction expectations – attempting to read a document that was just written in the same batch will return the old value because reads are not allowed. Use a transaction when you need to read before writing.
- Overlooking payload size – large fields (e.g., big arrays or blobs) can push the batch over 10 MiB even with fewer than 500 writes. Estimate size (e.g.,
JSON.stringifyof the write payload) before committing. - Assuming ordering across batches – if later batches depend on earlier ones, design your application to be idempotent or use a transaction for the dependent set.
Verifying the result
After a batch commits, confirm that all changes appear together:
- Run the batch code against a Firestore emulator (
firebase emulators:start --only firestore) or a test project. - Read the modified documents:
const aliceSnap = await db.collection('users').doc('alice').get();
const statsSnap = await db.collection('stats').doc('daily').get();
console.log('alice:', aliceSnap.data());
console.log('stats:', statsSnap.data());
If the batch succeeded, both documents show the new lastLogin timestamp and the incremented logins counter.
To test the limit, add a 501st write (e.g., another batch.set) and observe the FailedPrecondition error. Then split the operations into two batches (e.g., 250 writes each) and verify each commits successfully.
Practical size check: before committing, you can approximate the payload with
const payload = JSON.stringify(batch.toJSON()); // requires admin SDK v10+
console.log(`Approx. payload size: ${(payload.length / 1024).toFixed(2)} KB`);
If the size approaches 10 MiB, consider reducing field sizes or splitting the batch.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.