Transactions and batched writes Stay organized with collections Save and categorize content based on your preferences.
Cloud Firestore supports atomic operations for readingand writing data. In a set of atomic operations, either all of the operationssucceed, or none of them are applied. There are two types of atomic operationsinCloud Firestore:
- Transactions: atransaction is a set ofread and write operations on one or more documents.
- Batched Writes: abatched write is a set of write operations on one or more documents.
Updating data with transactions
Using theCloud Firestore client libraries, you can group multipleoperations into a single transaction. Transactions are useful when you want toupdate a field's value based on its current value, or the value of some otherfield.
A transaction consists of any number ofget() operations followed by any number of write operations such asset(),update(), ordelete(). In the case of a concurrent edit,Cloud Firestore runs the entire transaction again. For example, if atransaction reads documents and another client modifies any of those documents,Cloud Firestore retries the transaction. This feature ensures that thetransaction runs on up-to-date and consistent data.
Transactions never partiallyapply writes. All writes execute at the end of a successful transaction.
When using transactions, note that:
- Read operations must be executed before write operations.
- A function calling a transaction (transaction function) might runmore than once if a concurrent edit affects a document that the transactionreads.
- Transaction functions should not directly modify application state.
- Transactions will fail when the client is offline.
The following example shows how to create and run a transaction:
Web
import{runTransaction}from"firebase/firestore";try{awaitrunTransaction(db,async(transaction)=>{constsfDoc=awaittransaction.get(sfDocRef);if(!sfDoc.exists()){throw"Document does not exist!";}constnewPopulation=sfDoc.data().population+1;transaction.update(sfDocRef,{population:newPopulation});});console.log("Transaction successfully committed!");}catch(e){console.log("Transaction failed: ",e);}
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.// Create a reference to the SF doc.varsfDocRef=db.collection("cities").doc("SF");// Uncomment to initialize the doc.// sfDocRef.set({ population: 0 });returndb.runTransaction((transaction)=>{// This code may get re-run multiple times if there are conflicts.returntransaction.get(sfDocRef).then((sfDoc)=>{if(!sfDoc.exists){throw"Document does not exist!";}// Add one person to the city population.// Note: this could be done without a transaction// by updating the population using FieldValue.increment()varnewPopulation=sfDoc.data().population+1;transaction.update(sfDocRef,{population:newPopulation});});}).then(()=>{console.log("Transaction successfully committed!");}).catch((error)=>{console.log("Transaction failed: ",error);});
Swift
letsfReference=db.collection("cities").document("SF")do{let_=tryawaitdb.runTransaction({(transaction,errorPointer)->Any?inletsfDocument:DocumentSnapshotdo{trysfDocument=transaction.getDocument(sfReference)}catchletfetchErrorasNSError{errorPointer?.pointee=fetchErrorreturnnil}guardletoldPopulation=sfDocument.data()?["population"]as?Intelse{leterror=NSError(domain:"AppErrorDomain",code:-1,userInfo:[NSLocalizedDescriptionKey:"Unable to retrieve population from snapshot\(sfDocument)"])errorPointer?.pointee=errorreturnnil}// Note: this could be done without a transaction// by updating the population using FieldValue.increment()transaction.updateData(["population":oldPopulation+1],forDocument:sfReference)returnnil})print("Transaction successfully committed!")}catch{print("Transaction failed:\(error)")}
Objective-C
FIRDocumentReference*sfReference=[[self.dbcollectionWithPath:@"cities"]documentWithPath:@"SF"];[self.dbrunTransactionWithBlock:^id(FIRTransaction*transaction,NSError**errorPointer){FIRDocumentSnapshot*sfDocument=[transactiongetDocument:sfReferenceerror:errorPointer];if(*errorPointer!=nil){returnnil;}if(![sfDocument.data[@"population"]isKindOfClass:[NSNumberclass]]){*errorPointer=[NSErrorerrorWithDomain:@"AppErrorDomain"code:-1userInfo:@{NSLocalizedDescriptionKey:@"Unable to retreive population from snapshot"}];returnnil;}NSIntegeroldPopulation=[sfDocument.data[@"population"]integerValue];// Note: this could be done without a transaction// by updating the population using FieldValue.increment()[transactionupdateData:@{@"population":@(oldPopulation+1)}forDocument:sfReference];returnnil;}completion:^(idresult,NSError*error){if(error!=nil){NSLog(@"Transaction failed: %@",error);}else{NSLog(@"Transaction successfully committed!");}}];
Kotlin
valsfDocRef=db.collection("cities").document("SF")db.runTransaction{transaction->valsnapshot=transaction.get(sfDocRef)// Note: this could be done without a transaction// by updating the population using FieldValue.increment()valnewPopulation=snapshot.getDouble("population")!!+1transaction.update(sfDocRef,"population",newPopulation)// Successnull}.addOnSuccessListener{Log.d(TAG,"Transaction success!")}.addOnFailureListener{e->Log.w(TAG,"Transaction failure.",e)}
Java
finalDocumentReferencesfDocRef=db.collection("cities").document("SF");db.runTransaction(newTransaction.Function<Void>(){@OverridepublicVoidapply(@NonNullTransactiontransaction)throwsFirebaseFirestoreException{DocumentSnapshotsnapshot=transaction.get(sfDocRef);// Note: this could be done without a transaction// by updating the population using FieldValue.increment()doublenewPopulation=snapshot.getDouble("population")+1;transaction.update(sfDocRef,"population",newPopulation);// Successreturnnull;}}).addOnSuccessListener(newOnSuccessListener<Void>(){@OverridepublicvoidonSuccess(VoidaVoid){Log.d(TAG,"Transaction success!");}}).addOnFailureListener(newOnFailureListener(){@OverridepublicvoidonFailure(@NonNullExceptione){Log.w(TAG,"Transaction failure.",e);}});
Dart
finalsfDocRef=db.collection("cities").doc("SF");db.runTransaction((transaction)async{finalsnapshot=awaittransaction.get(sfDocRef);// Note: this could be done without a transaction// by updating the population using FieldValue.increment()finalnewPopulation=snapshot.get("population")+1;transaction.update(sfDocRef,{"population":newPopulation});}).then((value)=>print("DocumentSnapshot successfully updated!"),onError:(e)=>print("Error updating document$e"),);
Java
// Initialize docfinalDocumentReferencedocRef=db.collection("cities").document("SF");Citycity=newCity("SF");city.setCountry("USA");city.setPopulation(860000L);docRef.set(city).get();// run an asynchronous transactionApiFuture<Void>futureTransaction=db.runTransaction(transaction->{// retrieve document and increment population fieldDocumentSnapshotsnapshot=transaction.get(docRef).get();longoldPopulation=snapshot.getLong("population");transaction.update(docRef,"population",oldPopulation+1);returnnull;});// block on transaction operation using transaction.get()Python
transaction=db.transaction()city_ref=db.collection("cities").document("SF")@firestore.transactionaldefupdate_in_transaction(transaction,city_ref):snapshot=city_ref.get(transaction=transaction)transaction.update(city_ref,{"population":snapshot.get("population")+1})update_in_transaction(transaction,city_ref)Python
transaction=db.transaction()city_ref=db.collection("cities").document("SF")@firestore.async_transactionalasyncdefupdate_in_transaction(transaction,city_ref):snapshot=awaitcity_ref.get(transaction=transaction)transaction.update(city_ref,{"population":snapshot.get("population")+1})awaitupdate_in_transaction(transaction,city_ref)C++
DocumentReferencesf_doc_ref=db->Collection("cities").Document("SF");db->RunTransaction([sf_doc_ref](Transaction&transaction,std::string&out_error_message)->Error{Errorerror=Error::kErrorOk;DocumentSnapshotsnapshot=transaction.Get(sf_doc_ref,&error,&out_error_message);// Note: this could be done without a transaction by updating the// population using FieldValue::Increment().std::int64_tnew_population=snapshot.Get("population").integer_value()+1;transaction.Update(sf_doc_ref,{{"population", FieldValue::Integer(new_population)}});returnError::kErrorOk;}).OnCompletion([](constFuture<void>&future){if(future.error()==Error::kErrorOk){std::cout <<"Transaction success!" <<std::endl;}else{std::cout <<"Transaction failure: " <<future.error_message() <<std::endl;}});
Node.js
// Initialize documentconstcityRef=db.collection('cities').doc('SF');awaitcityRef.set({name:'San Francisco',state:'CA',country:'USA',capital:false,population:860000});try{awaitdb.runTransaction(async(t)=>{constdoc=awaitt.get(cityRef);// Add one person to the city population.// Note: this could be done without a transaction// by updating the population using FieldValue.increment()constnewPopulation=doc.data().population+1;t.update(cityRef,{population:newPopulation});});console.log('Transaction success!');}catch(e){console.log('Transaction failure:',e);}Go
import("context""log""cloud.google.com/go/firestore")funcrunSimpleTransaction(ctxcontext.Context,client*firestore.Client)error{// ...ref:=client.Collection("cities").Doc("SF")err:=client.RunTransaction(ctx,func(ctxcontext.Context,tx*firestore.Transaction)error{doc,err:=tx.Get(ref)// tx.Get, NOT ref.Get!iferr!=nil{returnerr}pop,err:=doc.DataAt("population")iferr!=nil{returnerr}returntx.Set(ref,map[string]interface{}{"population":pop.(int64)+1,},firestore.MergeAll)})iferr!=nil{// Handle any errors appropriately in this section.log.Printf("An error has occurred: %s",err)}returnerr}PHP
$cityRef = $db->collection('samples/php/cities')->document('SF');$db->runTransaction(function (Transaction $transaction) use ($cityRef) { $snapshot = $transaction->snapshot($cityRef); $newPopulation = $snapshot['population'] + 1; $transaction->update($cityRef, [ ['path' => 'population', 'value' => $newPopulation] ]);});Unity
DocumentReferencecityRef=db.Collection("cities").Document("SF");db.RunTransactionAsync(transaction=>{returntransaction.GetSnapshotAsync(cityRef).ContinueWith((snapshotTask)=>{DocumentSnapshotsnapshot=snapshotTask.Result;longnewPopulation=snapshot.GetValue<long>("Population")+1;Dictionary<string,object>updates=newDictionary<string,object>{{"Population",newPopulation}};transaction.Update(cityRef,updates);});});
C#
DocumentReferencecityRef=db.Collection("cities").Document("SF");awaitdb.RunTransactionAsync(asynctransaction=>{DocumentSnapshotsnapshot=awaittransaction.GetSnapshotAsync(cityRef);longnewPopulation=snapshot.GetValue<long>("Population")+1;Dictionary<string,object>updates=newDictionary<string,object>{{"Population",newPopulation}};transaction.Update(cityRef,updates);});Ruby
city_ref=firestore.doc"#{collection_path}/SF"firestore.transactiondo|tx|new_population=tx.get(city_ref).data[:population]+1puts"New population is#{new_population}."tx.updatecity_ref,{population:new_population}endPassing information out of transactions
Do not modify application state inside of your transaction functions. Doing sowill introduce concurrency issues, because transaction functions can runmultiple times and are not guaranteed to run on the UI thread. Instead, passinformation you need out of your transaction functions. The following examplebuilds on the previous example to show how to pass information out of atransaction:
Web
import{doc,runTransaction}from"firebase/firestore";// Create a reference to the SF doc.constsfDocRef=doc(db,"cities","SF");try{constnewPopulation=awaitrunTransaction(db,async(transaction)=>{constsfDoc=awaittransaction.get(sfDocRef);if(!sfDoc.exists()){throw"Document does not exist!";}constnewPop=sfDoc.data().population+1;if(newPop<=1000000){transaction.update(sfDocRef,{population:newPop});returnnewPop;}else{returnPromise.reject("Sorry! Population is too big");}});console.log("Population increased to ",newPopulation);}catch(e){// This will be a "population is too big" error.console.error(e);}
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.// Create a reference to the SF doc.varsfDocRef=db.collection("cities").doc("SF");db.runTransaction((transaction)=>{returntransaction.get(sfDocRef).then((sfDoc)=>{if(!sfDoc.exists){throw"Document does not exist!";}varnewPopulation=sfDoc.data().population+1;if(newPopulation<=1000000){transaction.update(sfDocRef,{population:newPopulation});returnnewPopulation;}else{returnPromise.reject("Sorry! Population is too big.");}});}).then((newPopulation)=>{console.log("Population increased to ",newPopulation);}).catch((err)=>{// This will be an "population is too big" error.console.error(err);});
Swift
letsfReference=db.collection("cities").document("SF")do{letobject=tryawaitdb.runTransaction({(transaction,errorPointer)->Any?inletsfDocument:DocumentSnapshotdo{trysfDocument=transaction.getDocument(sfReference)}catchletfetchErrorasNSError{errorPointer?.pointee=fetchErrorreturnnil}guardletoldPopulation=sfDocument.data()?["population"]as?Intelse{leterror=NSError(domain:"AppErrorDomain",code:-1,userInfo:[NSLocalizedDescriptionKey:"Unable to retrieve population from snapshot\(sfDocument)"])errorPointer?.pointee=errorreturnnil}// Note: this could be done without a transaction// by updating the population using FieldValue.increment()letnewPopulation=oldPopulation+1guardnewPopulation<=1000000else{leterror=NSError(domain:"AppErrorDomain",code:-2,userInfo:[NSLocalizedDescriptionKey:"Population\(newPopulation) too big"])errorPointer?.pointee=errorreturnnil}transaction.updateData(["population":newPopulation],forDocument:sfReference)returnnewPopulation})print("Population increased to\(object!)")}catch{print("Error updating population:\(error)")}
Objective-C
FIRDocumentReference*sfReference=[[self.dbcollectionWithPath:@"cities"]documentWithPath:@"SF"];[self.dbrunTransactionWithBlock:^id(FIRTransaction*transaction,NSError**errorPointer){FIRDocumentSnapshot*sfDocument=[transactiongetDocument:sfReferenceerror:errorPointer];if(*errorPointer!=nil){returnnil;}if(![sfDocument.data[@"population"]isKindOfClass:[NSNumberclass]]){*errorPointer=[NSErrorerrorWithDomain:@"AppErrorDomain"code:-1userInfo:@{NSLocalizedDescriptionKey:@"Unable to retreive population from snapshot"}];returnnil;}NSIntegerpopulation=[sfDocument.data[@"population"]integerValue];population++;if(population>=1000000){*errorPointer=[NSErrorerrorWithDomain:@"AppErrorDomain"code:-2userInfo:@{NSLocalizedDescriptionKey:@"Population too big"}];return@(population);}[transactionupdateData:@{@"population":@(population)}forDocument:sfReference];returnnil;}completion:^(idresult,NSError*error){if(error!=nil){NSLog(@"Transaction failed: %@",error);}else{NSLog(@"Population increased to %@",result);}}];
Kotlin
valsfDocRef=db.collection("cities").document("SF")db.runTransaction{transaction->valsnapshot=transaction.get(sfDocRef)valnewPopulation=snapshot.getDouble("population")!!+1if(newPopulation<=1000000){transaction.update(sfDocRef,"population",newPopulation)newPopulation}else{throwFirebaseFirestoreException("Population too high",FirebaseFirestoreException.Code.ABORTED,)}}.addOnSuccessListener{result->Log.d(TAG,"Transaction success:$result")}.addOnFailureListener{e->Log.w(TAG,"Transaction failure.",e)}
Java
finalDocumentReferencesfDocRef=db.collection("cities").document("SF");db.runTransaction(newTransaction.Function<Double>(){@OverridepublicDoubleapply(@NonNullTransactiontransaction)throwsFirebaseFirestoreException{DocumentSnapshotsnapshot=transaction.get(sfDocRef);doublenewPopulation=snapshot.getDouble("population")+1;if(newPopulation<=1000000){transaction.update(sfDocRef,"population",newPopulation);returnnewPopulation;}else{thrownewFirebaseFirestoreException("Population too high",FirebaseFirestoreException.Code.ABORTED);}}}).addOnSuccessListener(newOnSuccessListener<Double>(){@OverridepublicvoidonSuccess(Doubleresult){Log.d(TAG,"Transaction success: "+result);}}).addOnFailureListener(newOnFailureListener(){@OverridepublicvoidonFailure(@NonNullExceptione){Log.w(TAG,"Transaction failure.",e);}});
Dart
finalsfDocRef=db.collection("cities").doc("SF");db.runTransaction((transaction){returntransaction.get(sfDocRef).then((sfDoc){finalnewPopulation=sfDoc.get("population")+1;transaction.update(sfDocRef,{"population":newPopulation});returnnewPopulation;});}).then((newPopulation)=>print("Population increased to$newPopulation"),onError:(e)=>print("Error updating document$e"),);
Java
finalDocumentReferencedocRef=db.collection("cities").document("SF");ApiFuture<String>futureTransaction=db.runTransaction(transaction->{DocumentSnapshotsnapshot=transaction.get(docRef).get();LongnewPopulation=snapshot.getLong("population")+1;// conditionally update based on current populationif(newPopulation<=1000000L){transaction.update(docRef,"population",newPopulation);return"Population increased to "+newPopulation;}else{thrownewException("Sorry! Population is too big.");}});// Print information retrieved from transactionSystem.out.println(futureTransaction.get());Python
transaction=db.transaction()city_ref=db.collection("cities").document("SF")@firestore.transactionaldefupdate_in_transaction(transaction,city_ref):snapshot=city_ref.get(transaction=transaction)new_population=snapshot.get("population")+1ifnew_population <1000000:transaction.update(city_ref,{"population":new_population})returnTrueelse:returnFalseresult=update_in_transaction(transaction,city_ref)ifresult:print("Population updated")else:print("Sorry! Population is too big.")Python
transaction=db.transaction()city_ref=db.collection("cities").document("SF")@firestore.async_transactionalasyncdefupdate_in_transaction(transaction,city_ref):snapshot=awaitcity_ref.get(transaction=transaction)new_population=snapshot.get("population")+1ifnew_population <1000000:transaction.update(city_ref,{"population":new_population})returnTrueelse:returnFalseresult=awaitupdate_in_transaction(transaction,city_ref)ifresult:print("Population updated")else:print("Sorry! Population is too big.")C++
// This is not yet supported.Node.js
constcityRef=db.collection('cities').doc('SF');try{constres=awaitdb.runTransaction(asynct=>{constdoc=awaitt.get(cityRef);constnewPopulation=doc.data().population+1;if(newPopulation<=1000000){awaitt.update(cityRef,{population:newPopulation});return`Population increased to${newPopulation}`;}else{throw'Sorry! Population is too big.';}});console.log('Transaction success',res);}catch(e){console.log('Transaction failure:',e);}Go
import("context""errors""log""cloud.google.com/go/firestore")funcinfoTransaction(ctxcontext.Context,client*firestore.Client)(int64,error){varupdatedPopint64ref:=client.Collection("cities").Doc("SF")err:=client.RunTransaction(ctx,func(ctxcontext.Context,tx*firestore.Transaction)error{doc,err:=tx.Get(ref)iferr!=nil{returnerr}pop,err:=doc.DataAt("population")iferr!=nil{returnerr}newpop:=pop.(int64)+1ifnewpop<=1000000{err:=tx.Set(ref,map[string]interface{}{"population":newpop,},firestore.MergeAll)iferr==nil{updatedPop=newpop}returnerr}returnerrors.New("population is too big")})iferr!=nil{// Handle any errors in an appropriate way, such as returning them.log.Printf("An error has occurred: %s",err)}returnupdatedPop,err}PHP
$cityRef = $db->collection('samples/php/cities')->document('SF');$transactionResult = $db->runTransaction(function (Transaction $transaction) use ($cityRef) { $snapshot = $transaction->snapshot($cityRef); $newPopulation = $snapshot['population'] + 1; if ($newPopulation <= 1000000) { $transaction->update($cityRef, [ ['path' => 'population', 'value' => $newPopulation] ]); return true; } else { return false; }});if ($transactionResult) { printf('Population updated successfully.' . PHP_EOL);} else { printf('Sorry! Population is too big.' . PHP_EOL);}Unity
DocumentReferencecityRef=db.Collection("cities").Document("SF");db.RunTransactionAsync(transaction=>{returntransaction.GetSnapshotAsync(cityRef).ContinueWith((task)=>{longnewPopulation=task.Result.GetValue<long>("Population")+1;if(newPopulation<=1000000){Dictionary<string,object>updates=newDictionary<string,object>{{"Population",newPopulation}};transaction.Update(cityRef,updates);returntrue;}else{returnfalse;}});}).ContinueWith((transactionResultTask)=>{if(transactionResultTask.Result){Console.WriteLine("Population updated successfully.");}else{Console.WriteLine("Sorry! Population is too big.");}});
C#
DocumentReferencecityRef=db.Collection("cities").Document("SF");booltransactionResult=awaitdb.RunTransactionAsync(asynctransaction=>{DocumentSnapshotsnapshot=awaittransaction.GetSnapshotAsync(cityRef);longnewPopulation=snapshot.GetValue<long>("Population")+1;if(newPopulation<=1000000){Dictionary<string,object>updates=newDictionary<string,object>{{"Population",newPopulation}};transaction.Update(cityRef,updates);returntrue;}else{returnfalse;}});if(transactionResult){Console.WriteLine("Population updated successfully.");}else{Console.WriteLine("Sorry! Population is too big.");}Ruby
city_ref=firestore.doc"#{collection_path}/SF"updated=firestore.transactiondo|tx|new_population=tx.get(city_ref).data[:population]+1ifnew_population <1_000_000tx.updatecity_ref,{population:new_population}trueendendifupdatedputs"Population updated!"elseputs"Sorry! Population is too big."endTransaction failure
A transaction can fail for the following reasons:
- The transaction contains read operations after write operations.Read operations must always be executed before any write operations.
- The transaction read a document that was modified outside of the transaction.In this case, the transaction automatically runs again. Thetransaction is retried a finite number of times.
The transaction exceeded the maximum request size of 10 MiB.
Transaction size depends on the sizes of documents and index entriesmodified by the transaction. For a delete operation, this includes the sizeof the target document and the sizes of the index entries deleted inresponse to the operation.
The transaction exceeded the lock deadline (20 seconds).Cloud Firestore automatically releases locks if a transaction cannot complete in time.
The transaction exceeds the 270-second timelimit or the 60-second idle expiration time. If no activity (reads or writes) occurs within the transaction, it will time out and fail.
A failed transaction returns an error and does not write anything to thedatabase. You do not need to roll back the transaction;Cloud Firestoredoes this automatically.
Batched writes
If you do not need to read any documents in your operation set, you can executemultiple write operations as a single batch that contains any combination ofset(),update(), ordelete() operations.Each operation in the batch counts separately towards yourCloud Firestore usage. A batch of writes completesatomically and can write to multiple documents. The followingexample shows how to build and commit a write batch:
Web
import{writeBatch,doc}from"firebase/firestore";// Get a new write batchconstbatch=writeBatch(db);// Set the value of 'NYC'constnycRef=doc(db,"cities","NYC");batch.set(nycRef,{name:"New York City"});// Update the population of 'SF'constsfRef=doc(db,"cities","SF");batch.update(sfRef,{"population":1000000});// Delete the city 'LA'constlaRef=doc(db,"cities","LA");batch.delete(laRef);// Commit the batchawaitbatch.commit();
Web
Learn more about the tree-shakeable modular Web API and its advantages over the namespaced API.// Get a new write batchvarbatch=db.batch();// Set the value of 'NYC'varnycRef=db.collection("cities").doc("NYC");batch.set(nycRef,{name:"New York City"});// Update the population of 'SF'varsfRef=db.collection("cities").doc("SF");batch.update(sfRef,{"population":1000000});// Delete the city 'LA'varlaRef=db.collection("cities").doc("LA");batch.delete(laRef);// Commit the batchbatch.commit().then(()=>{// ...});
Swift
// Get new write batchletbatch=db.batch()// Set the value of 'NYC'letnycRef=db.collection("cities").document("NYC")batch.setData([:],forDocument:nycRef)// Update the population of 'SF'letsfRef=db.collection("cities").document("SF")batch.updateData(["population":1000000],forDocument:sfRef)// Delete the city 'LA'letlaRef=db.collection("cities").document("LA")batch.deleteDocument(laRef)// Commit the batchdo{tryawaitbatch.commit()print("Batch write succeeded.")}catch{print("Error writing batch:\(error)")}
Objective-C
// Get new write batchFIRWriteBatch*batch=[self.dbbatch];// Set the value of 'NYC'FIRDocumentReference*nycRef=[[self.dbcollectionWithPath:@"cities"]documentWithPath:@"NYC"];[batchsetData:@{}forDocument:nycRef];// Update the population of 'SF'FIRDocumentReference*sfRef=[[self.dbcollectionWithPath:@"cities"]documentWithPath:@"SF"];[batchupdateData:@{@"population":@1000000}forDocument:sfRef];// Delete the city 'LA'FIRDocumentReference*laRef=[[self.dbcollectionWithPath:@"cities"]documentWithPath:@"LA"];[batchdeleteDocument:laRef];// Commit the batch[batchcommitWithCompletion:^(NSError*_Nullableerror){if(error!=nil){NSLog(@"Error writing batch %@",error);}else{NSLog(@"Batch write succeeded.");}}];
Kotlin
valnycRef=db.collection("cities").document("NYC")valsfRef=db.collection("cities").document("SF")vallaRef=db.collection("cities").document("LA")// Get a new write batch and commit all write operationsdb.runBatch{batch->// Set the value of 'NYC'batch.set(nycRef,City())// Update the population of 'SF'batch.update(sfRef,"population",1000000L)// Delete the city 'LA'batch.delete(laRef)}.addOnCompleteListener{// ...}
Java
// Get a new write batchWriteBatchbatch=db.batch();// Set the value of 'NYC'DocumentReferencenycRef=db.collection("cities").document("NYC");batch.set(nycRef,newCity());// Update the population of 'SF'DocumentReferencesfRef=db.collection("cities").document("SF");batch.update(sfRef,"population",1000000L);// Delete the city 'LA'DocumentReferencelaRef=db.collection("cities").document("LA");batch.delete(laRef);// Commit the batchbatch.commit().addOnCompleteListener(newOnCompleteListener<Void>(){@OverridepublicvoidonComplete(@NonNullTask<Void>task){// ...}});
Dart
// Get a new write batchfinalbatch=db.batch();// Set the value of 'NYC'varnycRef=db.collection("cities").doc("NYC");batch.set(nycRef,{"name":"New York City"});// Update the population of 'SF'varsfRef=db.collection("cities").doc("SF");batch.update(sfRef,{"population":1000000});// Delete the city 'LA'varlaRef=db.collection("cities").doc("LA");batch.delete(laRef);// Commit the batchbatch.commit().then((_){// ...});
Java
// Get a new write batchWriteBatchbatch=db.batch();// Set the value of 'NYC'DocumentReferencenycRef=db.collection("cities").document("NYC");batch.set(nycRef,newCity());// Update the population of 'SF'DocumentReferencesfRef=db.collection("cities").document("SF");batch.update(sfRef,"population",1000000L);// Delete the city 'LA'DocumentReferencelaRef=db.collection("cities").document("LA");batch.delete(laRef);// asynchronously commit the batchApiFuture<List<WriteResult>>future=batch.commit();// ...// future.get() blocks on batch commit operationfor(WriteResultresult:future.get()){System.out.println("Update time : "+result.getUpdateTime());}Python
batch=db.batch()# Set the data for NYCnyc_ref=db.collection("cities").document("NYC")batch.set(nyc_ref,{"name":"New York City"})# Update the population for SFsf_ref=db.collection("cities").document("SF")batch.update(sf_ref,{"population":1000000})# Delete DENden_ref=db.collection("cities").document("DEN")batch.delete(den_ref)# Commit the batchbatch.commit()Python
batch=db.batch()# Set the data for NYCnyc_ref=db.collection("cities").document("NYC")batch.set(nyc_ref,{"name":"New York City"})# Update the population for SFsf_ref=db.collection("cities").document("SF")batch.update(sf_ref,{"population":1000000})# Delete DENden_ref=db.collection("cities").document("DEN")batch.delete(den_ref)# Commit the batchawaitbatch.commit()C++
// Get a new write batchWriteBatchbatch=db->batch();// Set the value of 'NYC'DocumentReferencenyc_ref=db->Collection("cities").Document("NYC");batch.Set(nyc_ref,{});// Update the population of 'SF'DocumentReferencesf_ref=db->Collection("cities").Document("SF");batch.Update(sf_ref,{{"population", FieldValue::Integer(1000000)}});// Delete the city 'LA'DocumentReferencela_ref=db->Collection("cities").Document("LA");batch.Delete(la_ref);// Commit the batchbatch.Commit().OnCompletion([](constFuture<void>&future){if(future.error()==Error::kErrorOk){std::cout <<"Write batch success!" <<std::endl;}else{std::cout <<"Write batch failure: " <<future.error_message() <<std::endl;}});
Node.js
// Get a new write batchconstbatch=db.batch();// Set the value of 'NYC'constnycRef=db.collection('cities').doc('NYC');batch.set(nycRef,{name:'New York City'});// Update the population of 'SF'constsfRef=db.collection('cities').doc('SF');batch.update(sfRef,{population:1000000});// Delete the city 'LA'constlaRef=db.collection('cities').doc('LA');batch.delete(laRef);// Commit the batchawaitbatch.commit();Go
import("context""log""cloud.google.com/go/firestore")funcbatchWrite(ctxcontext.Context,client*firestore.Client)error{// Get a new write batch.batch:=client.Batch()// Set the value of "NYC".nycRef:=client.Collection("cities").Doc("NYC")batch.Set(nycRef,map[string]interface{}{"name":"New York City",})// Update the population of "SF".sfRef:=client.Collection("cities").Doc("SF")batch.Set(sfRef,map[string]interface{}{"population":1000000,},firestore.MergeAll)// Delete the city "LA".laRef:=client.Collection("cities").Doc("LA")batch.Delete(laRef)// Commit the batch._,err:=batch.Commit(ctx)iferr!=nil{// Handle any errors in an appropriate way, such as returning them.log.Printf("An error has occurred: %s",err)}returnerr}PHP
$batch = $db->bulkWriter();# Set the data for NYC$nycRef = $db->collection('samples/php/cities')->document('NYC');$batch->set($nycRef, [ 'name' => 'New York City']);# Update the population for SF$sfRef = $db->collection('samples/php/cities')->document('SF');$batch->update($sfRef, [ ['path' => 'population', 'value' => 1000000]]);# Delete LA$laRef = $db->collection('samples/php/cities')->document('LA');$batch->delete($laRef);# Commit the batch$batch->commit();Unity
WriteBatchbatch=db.StartBatch();// Set the data for NYCDocumentReferencenycRef=db.Collection("cities").Document("NYC");Dictionary<string,object>nycData=newDictionary<string,object>{{"name","New York City"}};batch.Set(nycRef,nycData);// Update the population for SFDocumentReferencesfRef=db.Collection("cities").Document("SF");Dictionary<string,object>updates=newDictionary<string,object>{{"Population",1000000}};batch.Update(sfRef,updates);// Delete LADocumentReferencelaRef=db.Collection("cities").Document("LA");batch.Delete(laRef);// Commit the batchbatch.CommitAsync();
C#
WriteBatchbatch=db.StartBatch();// Set the data for NYCDocumentReferencenycRef=db.Collection("cities").Document("NYC");Dictionary<string,object>nycData=newDictionary<string,object>{{"name","New York City"}};batch.Set(nycRef,nycData);// Update the population for SFDocumentReferencesfRef=db.Collection("cities").Document("SF");Dictionary<string,object>updates=newDictionary<string,object>{{"Population",1000000}};batch.Update(sfRef,updates);// Delete LADocumentReferencelaRef=db.Collection("cities").Document("LA");batch.Delete(laRef);// Commit the batchawaitbatch.CommitAsync();Ruby
firestore.batchdo|b|# Set the data for NYCb.set"#{collection_path}/NYC",{name:"New York City"}# Update the population for SFb.update"#{collection_path}/SF",{population:1_000_000}# Delete LAb.delete"#{collection_path}/LA"endLike transactions, batched writes are atomic. Unlike transactions, batchedwrites do not need to ensure that read documents remain un-modified which leadsto fewer failure cases. They are not subject to retries orto failures from too many retries. Batched writes execute even when theuser's device is offline.
A batched write with hundreds of documents might require many index updatesand might exceed the limit on transaction size. In this case, reduce the number ofdocuments per batch. To write a large number of documents, consider using abulk writer or parallelized individual writes instead.
Note: For bulk data entry, use aserver client library withparallelized individual writes. Batched writes perform better than serializedwrites but not better than parallel writes. You should use a server clientlibrary for bulk data operations and not a mobile/web SDK.Data validation for atomic operations
For mobile/web client libraries, you can validate data usingCloud Firestore Security Rules. You can ensure that related documents arealways updated atomically and always as part of a transaction or batched write.Use thegetAfter() security rule function to access and validatethe state of a document after a set of operations completes butbeforeCloud Firestore commits the operations.
For example, imagine that the database for thecities example also contains acountries collection. Eachcountry document uses alast_updated field tokeep track of the last time any city related to that country was updated. Thefollowing security rules require that an update to acity document must alsoatomically update the related country'slast_updated field:
servicecloud.firestore{match/databases/{database}/documents{//Ifyouupdateacitydoc,youmustalso//updatetherelatedcountry'slast_updatedfield.match/cities/{city}{allowwrite:ifrequest.auth!=null&&getAfter(/databases/$(database)/documents/countries/$(request.resource.data.country)).data.last_updated==request.time;}match/countries/{country}{allowwrite:ifrequest.auth!=null;}}}
Security rules limits
In security rules for transactions or batched writes, there is alimitof 20 document access calls for the entire atomic operation in addition to thenormal 10 call limit for each single document operation in the batch.
For example, consider the following rules for a chat application:
servicecloud.firestore{match/databases/{db}/documents{functionprefix(){return/databases/{db}/documents;}match/chatroom/{roomId}{allowread,write:ifrequest.auth!=null&&roomIdinget(/$(prefix())/users/$(request.auth.uid)).data.chats||exists(/$(prefix())/admins/$(request.auth.uid));}match/users/{userId}{allowread,write:ifrequest.auth!=null&&request.auth.uid==userId||exists(/$(prefix())/admins/$(request.auth.uid));}match/admins/{userId}{allowread,write:ifrequest.auth!=null&&exists(/$(prefix())/admins/$(request.auth.uid));}}}
The snippets below illustrate the number of document access calls used fora few data access patterns:
//0documentaccesscallsused,becausetherulesevaluationshort-circuits//beforetheexists()callisinvoked.db.collection('user').doc('myuid').get(...);//1documentaccesscallused.Themaximumtotalallowedforthiscall//is10,becauseitisasingledocumentrequest.db.collection('chatroom').doc('mygroup').get(...);//Initializingawritebatch...varbatch=db.batch();//2documentaccesscallsused,10allowed.vargroup1Ref=db.collection("chatroom").doc("group1");batch.set(group1Ref,{msg:"Hello, from Admin!"});//1documentaccesscallused,10allowed.varnewUserRef=db.collection("users").doc("newuser");batch.update(newUserRef,{"lastSignedIn":newDate()});//1documentaccesscallused,10allowed.varremovedAdminRef=db.collection("admin").doc("otheruser");batch.delete(removedAdminRef);//Thebatchusedatotalof2+1+1=4documentaccesscalls,outofatotal//20allowed.batch.commit();
For more information on how to resolve latency issues caused by large writes and batched writes, errors due to contention from overlapping transactions, and other issues consider checking out thetroubleshooting page.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2026-02-06 UTC.