Trouble with “forEach” with Firebase Firestore .where() .get() Snapshots

I can only give an example of my code, since my real code is too big. I’m working with Firebase Cloud Functions and Firebase Firestore.

First I define a returnData dictionary (within my Firebase Cloud Function):

let returnData = {};

Then I get() “snapshots” of my db:

const db = admin.firestore();
const snapshotUsers = await db
      .collection("users")
      .where("followers", "array-contains", exampleId)
      .get();

Then, I’m trying to populate the returnData dictionary:

snapshotUsers.forEach(async (docUsers) => {
    await db
    .collection("users")
    .doc(docUsers.id)
    .collection("profileProjects")
    .listDocuments()
    .then(async (documentRefsProjects) => {
        if (!(Object.keys(documentRefsProjects).length === 0)) {
            return await db.getAll(...documentRefsProjects);
        }
    })
    .then(async (documentSnapshotsProjects) => {
        if (!!documentSnapshotsProject) {
            for (let documentSnapshotProject of documentSnapshotsProjects) {
                if (documentSnapshotProject.exists) {
                    // ----------- Populate here ----------- 
                    returnData = {
                        ...returnData,
                        [documentSnapshotProject.id]: {
                            followers: docUsers.data().followers,
                        }
                    };
                    // -------------------------------------- 
                }
            }
        }
    })
});
// Send response when forEach is done with returnData
res.status(201).send({ returnData: returnData });

Problem is: once it successfully populates returnData, it then throws that data away and res.status(201).send({ returnData: returnData }); returns the initial empty dictionary

I understand that: “forEach() executes the callback function once for each array element; unlike map() or reduce() it always returns the value undefined and is not chainable”, but I seems like I have no other option than to use forEach for firestore, so I’m stuck.

I tried using the map() function instead, but Firestore returns TypeError: snapshotUsers.map is not a function.

I even tried doing a manual for…in and for…of loop, but those also gave me errors when I would try to call id on docUsers:

for (let docUsers of snapshotUsers) {
    await db
        .collection("users")
        .doc(snapshotUsers[docUsers].id) // <---- error'd here

I’m having a real tough time here since the only way I can test this is deploying the Firebase Cloud Function every time.

I wondering if anyone can help me out here. Any advice or solution helps.

Answer

I’m having a real tough time here since the only way I can test this is deploying the Firebase Cloud Function every time.

check it: https://firebase.google.com/docs/hosting/test-preview-deploy

I used to work with:

firebase serve --only functions

And it just work fine.

For your problem…

Your foreach is ending before your returnData is populated. Foreach has one argument that show the current data index in your array.

So, in your success callback, you can check if it is the last array’s data then just send your response.

Something like this:

let returnData;
xpto.forEach((data,index)=>{
    db.then(()=>{
        //populates your returnData
        if(index === xpto.lenght-1) res.status(201).send({ returnData })
    })
})

Edit with askers experience:

for some reason didn’t work with the snapshots, I had to initialize an index outside the forEach and increment it within the .then() function.