Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Functions returns and promises do not exit the function

I am still a beginner in the Firebase world and I have been trying to figure out what the problem is with the below code but I failed in all possible ways.

The code is supposed to retrieve the uid from the user profile in the database, then use it to update the authentication profile, then again to update the database profile if the authentication profile update was successful.

In index.js I have defined an exported function to deal with POSTed params from HTML forms. The code below defines the handler function in another module file:

 exports.auUpdateUserByEmail = (req, res) => {
  // This handler function will retrieve the POSTed params of user profile
  // and will attempt to update the existing user authentication as well as
  // the database profiles.
  //
  // This function accepts the following params:
  // 1. User email   // 2. Phone number   // 3. password   // 4. Display name
  // 5. Photo url   // 6. Disabled Flag
  //

  var db = admin.firestore();

  var uEmail = req.body.userEmail;
  var dName = req.body.displayName;
  var userId = "";

  var newuser = {
    displayName: dName
  }

  console.log("Email passed: " + uEmail);

  // Fetch the user UID by user email...
  res.write('User UID: ' + userId);
  console.log('User UID: ' + userId);

  // attempt to update the user authentication profile...
  return db.collection('Users').where('email', '==', email).get()
  .then(snapshot => {
    snapshot.forEach(doc => {
        var d = doc.data();
        console.log("doc.id: " + doc.id + " - d.email: " + d.email);
        if(d.email == email)
        {
          userId = d.uid;
        }
    });

    return admin.auth().updateUser(userId, newuser);
  }).then(function(userRecord) {
    // The updating was successful... Attempt to update User Details in
    // database User Profile...
    console.log("User Updated Successfully. UID: " + userRecord.uid);
    retUid = userRecord.uid;

    // Create a reference to the Users Database...
    var docRef = db.collection('Users');

    // Update the user profile document.
    return docRef.doc(userRecord.uid).update(newuser);
  }).then(result => {
    // everything went fine... return User UID as a successful result...
    res.write(userId);

    return res.end();

  }).catch(function(error) {
    console.log("doc.update - Error updating user profile in database:", error);
    return res.end();

  });
}

In index.js, I have the following exports definition:

var appAuth = express();
//Here we are configuring express to use body-parser as middle-ware.
appAuth.use(bodyParser.urlencoded({ extended: false }));
appAuth.use(bodyParser.json());

appAuth.post('/updateUserByEmail', authusers.auUpdateUserByEmail);

exports.usersAuthFunctions = functions.https.onRequest(appAuth);

I have to say that I got it to work fine to get the uid, update the auth profile, and then update database profile, but it keeps on waiting for the function return.

Appreciate your valuable help. Thanks.


I have updated the code as below and it does the jobs but returns a blank page as the HTTPS exits before the promises are complete which fires "Error: write after end" error.

var fetch_uid = db.collection('Users').where('email', '==', uEmail).get()
  .then(snapshot => {
    // var userId = snapshot.data.uid;

    snapshot.forEach(doc => {
        var d = doc.data();
        console.log("doc.id: " + doc.id + " - d.email: " + d.email);
        if(d.email == uEmail)
        {
          userId = d.uid;

          res.write('User UID: ' + userId);
          console.log('User UID: ' + userId);

        }
    });

    return admin.auth().updateUser(userId, newuser);
  }).then(function(userRecord) {
    // The updating was successful... Attempt to update User Details in
    // database User Profile...
    console.log("User Updated Successfully. UID: " + userRecord.uid);
    retUid = userRecord.uid;

    // Create a reference to the Users Database...
    var docRef = db.collection('Users');

    // Update the user profile document.
    return docRef.doc(userRecord.uid).update(newuser);
  }).then(result => {
    // everything went fine... return User UID as a successful result...
    res.write(userId);

    return;

  }).catch(function(error) {
    console.log("doc.update - Error updating user profile in database:", error);
    return;

  });

  res.end();
like image 410
Badih Barakat Avatar asked Nov 16 '17 10:11

Badih Barakat


People also ask

How does the cloud function end?

You cannot disable a function. Just comment the function body. It would be a good practice to log the call in the console and then return null so you can keep track whenever the function is invoked.

What is promise in firebase?

A promise represents an operation and the future value it may return. It also lets you propagate errors similar to try/catch in synchronous code. You can read about promises in the Firebase SDK on The Firebase Blog, and promises in general on MDN.

Are firebase functions async?

Most of Firebase's APIs are asynchronous. This might be confusing at first: you're making a call, for example to fetch some data from Firestore, but you don't get a result back.

What is cold start in firebase?

Note: Several of the recommendations in this document center around what is known as a cold start. Functions are stateless, and the execution environment is often initialized from scratch, which is called a cold start. Cold starts can take significant amounts of time to complete.


1 Answers

A previous answer of mine on Cloud Functions for Firebase HTTP timeout might be of help here:

Cloud Functions triggered by HTTP requests need to be terminated by ending them with a send(), redirect(), or end(), otherwise they will continue running and reach the timeout.

From your code examples, it looks like your then(){} promise returns are ending with res.end(), but the entire function is returning the Promise from:

return db.collection('Users').where('email', '==', email).get()

Which could be stopping it from ending when you want it to. With HTTPS triggers, you don't need to return a Promise to keep the function running, only a result.

Try removing the return statement from this line:

db.collection('Users').where('email', '==', email).get()

Then you just need to ensure that all exit routes (or termination points) end with res.end() or similar, so currently you have 2 termination points:

  }).then(result => {
    // everything went fine... return User UID as a successful result...
    res.write(userId);

    res.status(200).end();
  }).catch(function(error) {
    console.log("doc.update - Error updating user profile in database:", error);

    res.status(500).end();
  });
like image 200
Grimthorr Avatar answered Nov 14 '22 22:11

Grimthorr