Firebase Cloud Functions – Issues with Promises

Tags: , , ,



When running my Firebase Cloud Function orgNew there seems to be something not working correctly with my promises and the order in which the code is executed.

Console log (shortened):

1: Function execution started
2: function checkLicenseKey() was successful
3: function checkPermissionsFromAuth() was successful 
4: result undefined
5: Error: TypeError: Cannot read property 'uid' of undefined // Points to marked spot in code 
6: Unhandled error Error: Unknown error code: failed to connect.
7: Function execution took 4663 ms, finished with status code: 500 
8: function getUserAuthObject() was successful { userObj is logged here }

My Firebase Cloud Function with some extra code (shortened):

exports.orgNew = functions
  .region("europe-west3")
  .https.onCall((data, context) => {

      var userObj;
      var orgID;
      return (
        checkLicenseKey(data.licenseKey)
          .then((result) => {
            if (!result.isValid) {
              throw new functions.https.HttpsError(
                "permission-denied",
                "Invalid license key used"
              );
            }
            return checkPermissionsFromAuth(context);
          })
          .then((result) => {
            return getUserAuthObject(context.auth.uid);
          })
          .then((result) => {
            console.info(result); // Error: result is undefined (line 4 in console)!
            userObj = result; // Therefore userObj is also undefined!
            return createOrganization(data, userObj);
 //  It seems that this gets executed even when if my userObj isn't ready yet!
          })
          .then((result) => {
            orgID = result.id;
            return loadDataFromTemplate(orgID, context);
          })
          .then((result) => {
            return addMember(orgID, userObj);
          })
          .then((result) => {
            return sendConfirmation(userObj.email, userObj.displayName);
          })
          .then((result) => {
            return { orgID, success: true };
          })
          .catch((err) => {
            // handle error
            functions.logger.error("Error:", err);
            throw new functions.https.HttpsError("failed to connect");
          })
      );
  });

function createOrganization(data, userObj) {
    const organisationRef = admin.firestore().collection("org");
    const document = {
      title: data.title,
      description: data.description,
      meta: {
        id: organisationRef.id,
        creator: {
// Error points to the following line, obviously this is because userObj is still undefined.
          creatorUID: userObj.uid,
          creatorEmail: userObj.email,
          creatorName: userObj.displayName,
        },
        createdAt: admin.firestore.FieldValue.serverTimestamp(),
      },
      },
    };
    return organisationRef
      .add(document)
      .then((organisationRef) => {
        functions.logger.info("function createOrganization() was successful");
        return organisationRef;
      })
      .catch((error) => {
        functions.logger.error("Error creating organization: ", error);
        throw new functions.https.HttpsError("failed to connect", error);
      });
  }
  
  function getUserAuthObject(uid) {
    admin
      .auth()
      .getUser(uid)
      .then((userRecord) => {
        const obj = {
          uid: userRecord.uid,
          email: userRecord.email,
          displayName: userRecord.displayName,
        };
        return obj;
      })
      .then((result) => {
        functions.logger.info("function getUserAuthObject() was successful", result);
        return result
      })
      .catch((err) => {
        functions.logger.error("Error fetching user data:", err);
        throw new functions.https.HttpsError("failed to connect");
      });
  }

All the other functions are not included here because they aren’t involved in the problem.

Any help is very much appreciated. Thanks!

Answer

From reading the code (i.e. without testing) it seems it is because you don’t return the Promises chain in the getUserAuthObject() function. Therefore userObj is undefined.

You should adapt it as follows:

  function getUserAuthObject(uid) {
    return admin  // !!! See the return here !!!
      .auth()
      .getUser(uid)
      .then((userRecord) => {
        const obj = {
          uid: userRecord.uid,
          email: userRecord.email,
          displayName: userRecord.displayName,
        };
        functions.logger.info("function getUserAuthObject() was successful", obj);
        return obj;
      })
      .catch((err) => {
        functions.logger.error("Error fetching user data:", err);
        throw new functions.https.HttpsError("failed to connect");
      });
  }

BTW, see how we can simplify the Promises chain in this function since you don’t need to do

    return obj;
  })
  .then((result) => {
    functions.logger.info("function getUserAuthObject() was successful", result);
    return result
  })

since obj does not return a Promise. It is not wrong, since then() returns a Promise but it is superfluous.



Source: stackoverflow