Entorno: nodejs , firebase-admin , firestore .
Estructura de la base de datos ( espacio ): 
Estructura de la base de datos ( usuario ): 
Creando un nuevo espacio (ejemplo):
// init data const userId = "someUserId"; // Create new space const spaceRef = await db.collection("spaces").add({ name: "SomeName" }); // Get spaceId spaceId = spaceRef.id; // Get user Doc for upate their spaces const userRef = await db.collection("users").doc(userId); // Add "spaceId" to user spaces list userRef.collection("spaces").doc(spaceId).set({ some: "data" }); // Create collection "members" in new space with "userId" spaceRef.collection("members").doc(userId).set({role: "OWNER"}) Pregunta : Quiero ejecutar este código dentro de una sola transacción de ejecución, pero como veo que las transacciones solo admiten lectura única y actualización múltiple, esto no me conviene, ya que obtengo el spaceId de espacio que necesito durante la ejecución del código.
Por qué quiero usar la transacción : en mi estructura de datos, se requiere la relación entre el espacio creado y la presencia de la ID de este espacio en el usuario. Si asumimos que ocurrió un error durante la ejecución de este código, por ejemplo, se creó el espacio, pero este espacio no se agregó dentro del perfil de usuario, entonces esto será un problema fatal en la estructura de mi base de datos.
Al igual que en otras bases de datos, las transacciones resuelven este problema, pero no sé cómo hacerlo con firestore.
¿Quizás conoce una mejor manera de protegerse de datos consistentes en este caso?
En realidad, no necesita una Transacción para eso, ya que no está leyendo documentos.
Con db.collection("users").doc(userId); en realidad no está leyendo un documento, simplemente está llamando "localmente" al método doc() para crear una DocumentReference . Este método no es asíncrono, por lo que no necesita usar await . Para leer el documento, usaría el método asincrónico get() .
Entonces, usar una escritura por lotes, que compromete atómicamente todas las operaciones de escritura pendientes en la base de datos, hará el truco:
const userId = 'someUserId'; const userRef = db.collection('users').doc(userId); const spaceRef = firestore.collection('spaces').doc(); const spaceId = spaceRef.id; const writeBatch = firestore.batch(); writeBatch.set(spaceRef, { name: "SomeName" }); writeBatch.set(userRef.collection("spaces").doc(spaceId), { some: "data" }); writeBatch.set(spaceRef.collection("members").doc(userId), {role: "OWNER"}); await writeBatch.commit(); Debe incluir este código en un bloque try/catch y, si la confirmación por lotes falla, podrá manejar esta situación en el bloque catch , sabiendo que no se ha confirmado ninguna escritura.