1

I'm trying to create a Firebase function that clones a subcollection from one user to another.

However, I'm having a problem with the last step: Adding subcollection documents to the new user.

Here is my function:

import { Firestore } from '@google-cloud/firestore';

exports.handler = async function(req: any, res: any, db: Firestore) {
    const fromUid = req.body.from;
    const toUid = req.body.to;
    let transactionCount = 0;

    const userToRef = await db
        .collection('users')
        .doc(toUid)
        .collection('transactions');
    await userToRef.add({ ...{ data: 'some-dummy-data' } }); // At this point I get write to the collection
    const transactions = await db
        .collection(`users/${fromUid}/transactions`)
        .get();

    const transactionsList: any[] = [];
    transactions.forEach((doc: any) => {
        const transaction = doc.data();
        transaction.uid = toUid;
        transactionsList.push(transaction);
        userToRef.add({
            // Here write to the collection doesnt work
            ...transaction
        });
        transactionCount++;
    });
    console.log(transactionsList);

    res.send(
        `Successfully cloned ${transactionCount} transactions from ${fromUid} to ${toUid}.`
    );
    return '';
};

In the code example above I'm doing two writes to the user subcollection (see code comments). First one is working OK and the second one is not.

Any idea why I cannot do write inside a forEach loop?

1 Answer 1

4

You should use Promise.all(), in order to wait for all the writes to the userToRef collection to complete before sending back the response to the client through the res object.

The following adapted code should work (untested).

However it is not clear for me what you should exactly do at the end, since we don't know how do you call this function (you probably call it from the Cloud Function itself?). You probably don't need to do return ''; since by doing res.send() you are actually terminating the function. For your reference, I've added below the entire code of a tested HTTPS Cloud Function that does a similar set of collection writes.

exports.handler = async function (req: any, res: any, db: Firestore) {
    const fromUid = req.body.from;
    const toUid = req.body.to;
    let transactionCount = 0;

    const userToRef = await db
        .collection('users')
        .doc(toUid)
        .collection('transactions');
    await userToRef.add({ ...{ data: 'some-dummy-data' } }); // At this point I get write to the collection
    const transactions = await db
        .collection(`users/${fromUid}/transactions`)
        .get();

    //const transactionsList: any[] = [];

    const promises: Promise<DocumentReference>[] = [];

    transactions.forEach((doc: any) => {
        const transaction = doc.data();
        transaction.uid = toUid;
        //transactionsList.push(transaction);
        promises.push(
            userToRef.add({
                // Here write to the collection doesnt work
                ...transaction
            }));
        transactionCount++;
    });
    //console.log(transactionsList);

    await Promise.all(promises);
    res.send(
        `Successfully cloned ${transactionCount} transactions from ${fromUid} to ${toUid}.`
    );
    //return '';
};

Here is the code of an HTTPS Cloud Function that implements a similar set of collection writes:

import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { DocumentReference } from '@google-cloud/firestore';

admin.initializeApp();

export const helloWorld = functions.https.onRequest(async (req, res) => {
  const db = admin.firestore();

  let writeCount = 0;

  const theArray: string[] = ['AA', 'BB', 'CC'];

  const promises: Promise<DocumentReference>[] = [];

  theArray.forEach((value: string) => {
    promises.push(db.collection('testCollection').add({ val: value }));
    writeCount++;
  });

  await Promise.all(promises);

  res.send(`Successfully wrote ${writeCount} documents.`);
});
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.