Based on your code, you are building this based on a very outdated tutorial.
In Firebase Functions V1.0.0 (April 2018), functions.config().firebase
was removed. It now resolves as undefined
so that the modern admin.initializeApp()
(with no arguments) functions properly. When invoked this way, the Admin SDK initializes based on the presence of the appropriate environment variables.
Next, you are using the Explicit Promise Construction Antipattern. In short, do not wrap your code inside new Promise((resolve, reject) => {})
.
As this functions code is likely to be deployed to Google Cloud Functions, which runs on Node 10/12/14 you can use modern ES2017 syntax like async
/await
, deconstruction patterns, import
, let
, const
and Promise.all
.
So this means the top of your script consists of:
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
admin.initializeApp();
As you need to fetch a list of players for each token given, you can break this logic out into it's own function:
/**
* For the given token, return an array of players linked with
* that token.
*/
async function getFootballPlayersWithToken(db, token) {
const playersWithTokenQuerySnapshot = await db
.collection("Football_Player_Data")
.where("Token", "==", token)
.get();
if (playersWithTokenQuerySnapshot.empty) {
console.log("Given token did not match any players: ", token);
return []; // or throw error
}
const playerArray = await Promise.all(
playersWithTokenQuerySnapshot.docs
.map((playerDoc) => {
const { Token, Jersey_Number, Multiplier, PlayerID, Position, Team } = playerDoc.data();
return { Token, Jersey_Number, Multiplier, PlayerID, Position, Team };
});
);
return playerArray;
}
In the above code block, it's important to note that playersWithTokenQuerySnapshot
is a QuerySnapshot
object, not an array. While forEach
is available on the QuerySnapshot
object, to use normal Array methods like .map
and .reduce
, you need to use the docs
property instead.
This makes your Cloud Function:
exports.getPlayerDataFromTokens = functions.https.onCall(async (data, context) => {
const { tokens } = data;
try {
const db = admin.firestore();
const playersGroupedByTokenArray = await Promise.all(
tokens.map(token => getFootballPlayersWithToken(db, token))
);
// playersGroupedByTokenArray is an array of arrays
// e.g. [[token1Player1, token1Player2], [token2Player1], [], ...]
// flatten this array
// Node 12+:
// const playerArray = playersGroupedByTokenArray.flat()
// Node 10+:
const playerArray = [];
playersGroupedByTokenArray.forEach(
groupOfPlayers => playerArray.push(...groupOfPlayers)
);
return playerArray;
} catch (error) {
console.error("Error finding players for one or more of these tokens: ", tokens.join(", "))
throw new functions.https.HttpsError(
"unknown",
"Could not get players for one or more of the tokens provided",
error.code || error.message
);
}
);
As shown in the above code block, for errors to be handled properly, you must wrap them in a HttpsError
as documented here.
If a token would return only one player, you can tweak the above get players function to just the following and then edit your Cloud Function as needed:
/**
* For the given token, return the matching player.
*/
async function getFootballPlayerByToken(db, token) {
const playersWithTokenQuerySnapshot = await db
.collection("Football_Player_Data")
.where("Token", "==", token)
.get();
if (playersWithTokenQuerySnapshot.empty) {
console.log("Given token did not match any players: ", token);
return null; // or throw error
}
const playerDoc = playersWithTokenQuerySnapshot.docs[0];
const { Token, Jersey_Number, Multiplier, PlayerID, Position, Team } = playerDoc.data();
return { Token, Jersey_Number, Multiplier, PlayerID, Position, Team };
}