Created
February 7, 2023 19:31
-
-
Save Bastianowicz/5fd606db75708fd557d27b23feed1f4a to your computer and use it in GitHub Desktop.
Nest.js Setup for decoupled Firestore Transactions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class FirebaseTransactionAdapter implements TransactionProviderPort { | |
private readonly logger = new Logger(FirebaseTransactionAdapter.name); | |
constructor( | |
@Inject(Firestore.prototype.runTransaction) | |
private readonly runTransaction: TransactionRunner, | |
) { | |
} | |
async executeTransaction( | |
callback: () => Promise<void> | |
): Promise<Result<void, DeleteErrorTypes>> { | |
try { | |
// transcations triggered down the stream will now all run via the same transaction workload | |
const result = await this.runTransaction(callback); | |
return ok(result); | |
} catch (e) { | |
const msg = "Error executing transaction"; | |
this.logger.error(msg, e); | |
return err(new WriteError(msg)); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const adminApp = admin.initializeApp(); | |
let transaction: Transaction | undefined; | |
export type TransactionLambda = (transaction: Transaction) => Promise<void>; | |
export type TransactionRunner = (callback: TransactionLambda) => Promise<void>; | |
@Global() | |
@Module({ | |
providers: [ | |
{ | |
provide: Firestore, | |
useValue: (() => { | |
// must be value and not factory so that settings is only being called once | |
const firestore = adminApp.firestore(); | |
firestore.settings({ignoreUndefinedProperties: true,}); | |
return firestore; | |
} | |
)() | |
}, | |
{ | |
provide: Firestore.prototype.runTransaction, | |
inject: [Firestore], | |
useFactory: (firestore: Firestore) => { | |
return async (updateFunction: TransactionLambda) => { | |
if(transaction) { | |
// if a transaction has already been triggered, just execute the lambda with existing transaction | |
await updateFunction(transaction); | |
} else { | |
// if this is the start of a transaction, start a new one... | |
await firestore.runTransaction(async (t) => { | |
// ... reference the transaction object | |
transaction = t; | |
// execute the lambda | |
await updateFunction(transaction); | |
// unset the transaction reference | |
transaction = undefined; | |
}); | |
} | |
}; | |
} | |
} | |
], | |
exports: [ | |
Firestore.prototype.runTransaction, | |
Firestore, | |
] | |
}) | |
export class UtilModule { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment