or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-types.mddatabase-operations.mdindex.mdmodular-api.mdoffline-network.mdquerying-filtering.mdrealtime-sync.mdtransactions-batches.md

transactions-batches.mddocs/

0

# Transactions & Batches

1

2

Atomic operations for ensuring data consistency across multiple operations. Supports both read-write transactions and write-only batches.

3

4

## Capabilities

5

6

### Transactions

7

8

Execute multiple operations atomically with read and write capabilities. Transactions ensure data consistency by retrying operations if data changes during execution.

9

10

```typescript { .api }

11

/**

12

* Run a transaction with automatic retries

13

* @param updateFunction - Function that performs transaction operations

14

* @returns Promise resolving to the transaction result

15

*/

16

runTransaction<T>(updateFunction: (transaction: FirebaseFirestoreTypes.Transaction) => Promise<T>): Promise<T>;

17

18

interface Transaction {

19

/**

20

* Read a document within the transaction

21

* @param documentRef - Document reference to read

22

* @returns Promise resolving to document snapshot

23

*/

24

get<T>(documentRef: FirebaseFirestoreTypes.DocumentReference<T>): Promise<FirebaseFirestoreTypes.DocumentSnapshot<T>>;

25

26

/**

27

* Set document data within the transaction

28

* @param documentRef - Document reference to set

29

* @param data - Document data to set

30

* @param options - Optional set options for merging

31

* @returns Transaction instance for chaining

32

*/

33

set<T>(

34

documentRef: FirebaseFirestoreTypes.DocumentReference<T>,

35

data: T,

36

options?: FirebaseFirestoreTypes.SetOptions

37

): FirebaseFirestoreTypes.Transaction;

38

39

/**

40

* Update document fields within the transaction

41

* @param documentRef - Document reference to update

42

* @param data - Partial data to update

43

* @returns Transaction instance for chaining

44

*/

45

update<T>(

46

documentRef: FirebaseFirestoreTypes.DocumentReference<T>,

47

data: Partial<T>

48

): FirebaseFirestoreTypes.Transaction;

49

50

/**

51

* Update specific fields using field paths within the transaction

52

* @param documentRef - Document reference to update

53

* @param field - Field path to update

54

* @param value - New field value

55

* @param moreFieldsAndValues - Additional field-value pairs

56

* @returns Transaction instance for chaining

57

*/

58

update<T>(

59

documentRef: FirebaseFirestoreTypes.DocumentReference<T>,

60

field: keyof T | FirebaseFirestoreTypes.FieldPath,

61

value: any,

62

...moreFieldsAndValues: any[]

63

): FirebaseFirestoreTypes.Transaction;

64

65

/**

66

* Delete a document within the transaction

67

* @param documentRef - Document reference to delete

68

* @returns Transaction instance for chaining

69

*/

70

delete(documentRef: FirebaseFirestoreTypes.DocumentReference): FirebaseFirestoreTypes.Transaction;

71

}

72

```

73

74

**Usage Examples:**

75

76

```typescript

77

import firestore from '@react-native-firebase/firestore';

78

79

// Transfer money between accounts atomically

80

async function transferMoney(fromAccountId: string, toAccountId: string, amount: number) {

81

const db = firestore();

82

83

return db.runTransaction(async (transaction) => {

84

// Read current balances

85

const fromAccountRef = db.collection('accounts').doc(fromAccountId);

86

const toAccountRef = db.collection('accounts').doc(toAccountId);

87

88

const fromAccountDoc = await transaction.get(fromAccountRef);

89

const toAccountDoc = await transaction.get(toAccountRef);

90

91

if (!fromAccountDoc.exists || !toAccountDoc.exists) {

92

throw new Error('One or both accounts do not exist');

93

}

94

95

const fromBalance = fromAccountDoc.data().balance;

96

const toBalance = toAccountDoc.data().balance;

97

98

if (fromBalance < amount) {

99

throw new Error('Insufficient funds');

100

}

101

102

// Perform the transfer

103

transaction.update(fromAccountRef, {

104

balance: fromBalance - amount,

105

lastTransactionAt: firestore.FieldValue.serverTimestamp()

106

});

107

108

transaction.update(toAccountRef, {

109

balance: toBalance + amount,

110

lastTransactionAt: firestore.FieldValue.serverTimestamp()

111

});

112

113

// Create transaction record

114

const transactionRef = db.collection('transactions').doc();

115

transaction.set(transactionRef, {

116

fromAccountId,

117

toAccountId,

118

amount,

119

createdAt: firestore.FieldValue.serverTimestamp(),

120

type: 'transfer'

121

});

122

123

return { transactionId: transactionRef.id, newFromBalance: fromBalance - amount };

124

});

125

}

126

127

// Increment counter with collision handling

128

async function incrementCounter(counterId: string) {

129

const db = firestore();

130

131

return db.runTransaction(async (transaction) => {

132

const counterRef = db.collection('counters').doc(counterId);

133

const counterDoc = await transaction.get(counterRef);

134

135

if (!counterDoc.exists) {

136

// Create new counter

137

transaction.set(counterRef, { count: 1 });

138

return 1;

139

} else {

140

// Increment existing counter

141

const newCount = counterDoc.data().count + 1;

142

transaction.update(counterRef, { count: newCount });

143

return newCount;

144

}

145

});

146

}

147

148

// Complex transaction with multiple operations

149

async function createOrderWithInventoryCheck(orderData: any) {

150

const db = firestore();

151

152

return db.runTransaction(async (transaction) => {

153

const results = [];

154

155

// Check inventory for all items

156

for (const item of orderData.items) {

157

const productRef = db.collection('products').doc(item.productId);

158

const productDoc = await transaction.get(productRef);

159

160

if (!productDoc.exists) {

161

throw new Error(`Product ${item.productId} not found`);

162

}

163

164

const availableStock = productDoc.data().stock;

165

if (availableStock < item.quantity) {

166

throw new Error(`Insufficient stock for product ${item.productId}`);

167

}

168

169

results.push({ productRef, currentStock: availableStock, orderQuantity: item.quantity });

170

}

171

172

// Update inventory

173

results.forEach(({ productRef, currentStock, orderQuantity }) => {

174

transaction.update(productRef, {

175

stock: currentStock - orderQuantity,

176

lastOrderAt: firestore.FieldValue.serverTimestamp()

177

});

178

});

179

180

// Create order

181

const orderRef = db.collection('orders').doc();

182

transaction.set(orderRef, {

183

...orderData,

184

status: 'confirmed',

185

createdAt: firestore.FieldValue.serverTimestamp()

186

});

187

188

return { orderId: orderRef.id };

189

});

190

}

191

```

192

193

### Write Batches

194

195

Execute multiple write operations atomically without reads. Batches are more efficient than transactions when you only need to write data.

196

197

```typescript { .api }

198

/**

199

* Create a new write batch

200

* @returns WriteBatch instance

201

*/

202

batch(): FirebaseFirestoreTypes.WriteBatch;

203

204

interface WriteBatch {

205

/**

206

* Set document data in the batch

207

* @param documentRef - Document reference to set

208

* @param data - Document data to set

209

* @param options - Optional set options for merging

210

* @returns WriteBatch instance for chaining

211

*/

212

set<T>(

213

documentRef: FirebaseFirestoreTypes.DocumentReference<T>,

214

data: T,

215

options?: FirebaseFirestoreTypes.SetOptions

216

): FirebaseFirestoreTypes.WriteBatch;

217

218

/**

219

* Update document fields in the batch

220

* @param documentRef - Document reference to update

221

* @param data - Partial data to update

222

* @returns WriteBatch instance for chaining

223

*/

224

update<T>(

225

documentRef: FirebaseFirestoreTypes.DocumentReference<T>,

226

data: Partial<T>

227

): FirebaseFirestoreTypes.WriteBatch;

228

229

/**

230

* Update specific fields using field paths in the batch

231

* @param documentRef - Document reference to update

232

* @param field - Field path to update

233

* @param value - New field value

234

* @param moreFieldsAndValues - Additional field-value pairs

235

* @returns WriteBatch instance for chaining

236

*/

237

update<T>(

238

documentRef: FirebaseFirestoreTypes.DocumentReference<T>,

239

field: keyof T | FirebaseFirestoreTypes.FieldPath,

240

value: any,

241

...moreFieldsAndValues: any[]

242

): FirebaseFirestoreTypes.WriteBatch;

243

244

/**

245

* Delete a document in the batch

246

* @param documentRef - Document reference to delete

247

* @returns WriteBatch instance for chaining

248

*/

249

delete(documentRef: FirebaseFirestoreTypes.DocumentReference): FirebaseFirestoreTypes.WriteBatch;

250

251

/**

252

* Commit all operations in the batch

253

* @returns Promise resolving when all operations complete

254

*/

255

commit(): Promise<void>;

256

}

257

```

258

259

**Usage Examples:**

260

261

```typescript

262

import firestore from '@react-native-firebase/firestore';

263

264

// Bulk user creation

265

async function createMultipleUsers(users: any[]) {

266

const db = firestore();

267

const batch = db.batch();

268

269

users.forEach(userData => {

270

const userRef = db.collection('users').doc(); // Auto-generate ID

271

batch.set(userRef, {

272

...userData,

273

createdAt: firestore.FieldValue.serverTimestamp()

274

});

275

});

276

277

await batch.commit();

278

console.log(`Created ${users.length} users successfully`);

279

}

280

281

// Bulk update operation

282

async function updateUserStatuses(userIds: string[], newStatus: string) {

283

const db = firestore();

284

const batch = db.batch();

285

286

userIds.forEach(userId => {

287

const userRef = db.collection('users').doc(userId);

288

batch.update(userRef, {

289

status: newStatus,

290

statusUpdatedAt: firestore.FieldValue.serverTimestamp()

291

});

292

});

293

294

await batch.commit();

295

console.log(`Updated ${userIds.length} user statuses`);

296

}

297

298

// Mixed operations in a batch

299

async function setupNewProject(projectData: any) {

300

const db = firestore();

301

const batch = db.batch();

302

303

// Create project document

304

const projectRef = db.collection('projects').doc();

305

batch.set(projectRef, {

306

...projectData,

307

createdAt: firestore.FieldValue.serverTimestamp(),

308

status: 'active'

309

});

310

311

// Create default settings

312

const settingsRef = db.collection('projects').doc(projectRef.id).collection('settings').doc('default');

313

batch.set(settingsRef, {

314

theme: 'light',

315

notifications: true,

316

language: 'en'

317

});

318

319

// Update user's project count

320

const userRef = db.collection('users').doc(projectData.ownerId);

321

batch.update(userRef, {

322

projectCount: firestore.FieldValue.increment(1),

323

lastProjectCreatedAt: firestore.FieldValue.serverTimestamp()

324

});

325

326

// Create activity log entry

327

const activityRef = db.collection('activities').doc();

328

batch.set(activityRef, {

329

type: 'project_created',

330

projectId: projectRef.id,

331

userId: projectData.ownerId,

332

timestamp: firestore.FieldValue.serverTimestamp()

333

});

334

335

await batch.commit();

336

return { projectId: projectRef.id };

337

}

338

339

// Batch with merge operations

340

async function updateUserProfiles(profileUpdates: Array<{ userId: string; profileData: any }>) {

341

const db = firestore();

342

const batch = db.batch();

343

344

profileUpdates.forEach(({ userId, profileData }) => {

345

const userRef = db.collection('users').doc(userId);

346

batch.set(userRef, {

347

profile: profileData,

348

updatedAt: firestore.FieldValue.serverTimestamp()

349

}, { merge: true }); // Merge with existing data

350

});

351

352

await batch.commit();

353

}

354

355

// Conditional batch operations

356

async function archiveOldPosts(cutoffDate: Date) {

357

const db = firestore();

358

359

// First, query for old posts

360

const oldPostsSnapshot = await db

361

.collection('posts')

362

.where('createdAt', '<', cutoffDate)

363

.where('archived', '==', false)

364

.limit(500) // Firestore batch limit

365

.get();

366

367

if (oldPostsSnapshot.empty) {

368

console.log('No posts to archive');

369

return;

370

}

371

372

const batch = db.batch();

373

374

oldPostsSnapshot.docs.forEach(doc => {

375

batch.update(doc.ref, {

376

archived: true,

377

archivedAt: firestore.FieldValue.serverTimestamp()

378

});

379

});

380

381

await batch.commit();

382

console.log(`Archived ${oldPostsSnapshot.size} posts`);

383

}

384

```

385

386

### Best Practices

387

388

**Transaction vs Batch Decision Guide:**

389

390

```typescript

391

// Use Transactions when:

392

// 1. You need to read data before writing

393

// 2. Operations depend on current document state

394

// 3. You need conditional logic based on existing data

395

396

// Example: Transfer money (requires reading current balances)

397

async function useTransaction() {

398

return firestore().runTransaction(async (transaction) => {

399

const accountDoc = await transaction.get(accountRef);

400

const currentBalance = accountDoc.data().balance;

401

402

if (currentBalance >= withdrawAmount) {

403

transaction.update(accountRef, { balance: currentBalance - withdrawAmount });

404

}

405

});

406

}

407

408

// Use Batches when:

409

// 1. Only performing write operations

410

// 2. Operations are independent of existing data

411

// 3. Need better performance for write-only operations

412

413

// Example: Bulk create or update operations

414

async function useBatch() {

415

const batch = firestore().batch();

416

417

documents.forEach(doc => {

418

const docRef = firestore().collection('items').doc();

419

batch.set(docRef, doc);

420

});

421

422

return batch.commit();

423

}

424

```

425

426

### Error Handling

427

428

```typescript { .api }

429

interface FirestoreError extends Error {

430

readonly code: FirebaseFirestoreTypes.FirestoreErrorCode;

431

readonly message: string;

432

}

433

```

434

435

**Error Handling Examples:**

436

437

```typescript

438

import firestore from '@react-native-firebase/firestore';

439

440

// Transaction error handling

441

async function safeTransfer(fromId: string, toId: string, amount: number) {

442

try {

443

const result = await firestore().runTransaction(async (transaction) => {

444

// Transaction logic here

445

return { success: true };

446

});

447

448

console.log('Transfer completed:', result);

449

} catch (error) {

450

switch (error.code) {

451

case 'aborted':

452

console.error('Transaction was aborted due to conflicting updates');

453

break;

454

case 'failed-precondition':

455

console.error('Transaction failed due to precondition failure');

456

break;

457

case 'permission-denied':

458

console.error('User does not have permission for this operation');

459

break;

460

default:

461

console.error('Transaction failed:', error.message);

462

}

463

throw error;

464

}

465

}

466

467

// Batch error handling

468

async function safeBatchWrite(operations: any[]) {

469

try {

470

const batch = firestore().batch();

471

472

operations.forEach(op => {

473

// Add operations to batch

474

});

475

476

await batch.commit();

477

console.log('Batch write completed successfully');

478

} catch (error) {

479

console.error('Batch write failed:', error.message);

480

481

if (error.code === 'invalid-argument') {

482

console.error('One or more operations in the batch are invalid');

483

}

484

485

throw error;

486

}

487

}

488

```

489

490

## Types

491

492

```typescript { .api }

493

interface SetOptions {

494

/**

495

* Whether to merge the data with existing document

496

*/

497

merge?: boolean;

498

499

/**

500

* Specific fields to merge (requires merge: true)

501

*/

502

mergeFields?: (string | FirebaseFirestoreTypes.FieldPath)[];

503

}

504

505

/**

506

* Maximum number of operations allowed in a single batch

507

*/

508

const MAX_BATCH_SIZE = 500;

509

510

/**

511

* Maximum number of documents that can be read in a single transaction

512

*/

513

const MAX_TRANSACTION_READS = 100;

514

```