I'm using firebase admin SDK to create new User to my web app.
// function to create user and store additional info in firestore
exports.createUser = functions
.https.onCall((data, context) => {
admin.auth().createUser({
phoneNumber: data.phoneNumber,
displayName: data.displayName,
}).then((user) => {
// store data in firestore
admin.firestore().collection("user").doc(user.uid).set({...data});
}).catch((err) => {
// handle error
});
});
When I call the function from the client (attach it to onClick event) I want to wait till user is successfully added in firestore and invoke the fetchUser function so I can see the new list of data with the newly added user. Currently fetchUser gets called and page refreshes but I cannot see the newly added user before refreshing it.
const createUser = functions.httpsCallable('createUser');
const createNewUser = () => {
createUser({
phoneNumber: "+1111111111111",
displayName: "test",
introduction: "testcreate",
})
.then((res) => {
fetchUser(); // fetchUser just fetches user data from firestore and rerenders page
})
.catch((err) => {
console.log(err);
});
};
To summarize, can I know when cloud function will complete its job and run a particular function or task ?
As you will see in the three videos about "JavaScript Promises" from the official Firebase video series you MUST return a Promise or a value in a background triggered, Pub/Sub or Callable Cloud Function when all the asynchronous operations are complete.
This has two linked effects:
To return a Promise, since you chain several asynchronous Firebase Admin SDK methods (which return Promises), you need to return the Promise chain as follows:
exports.createUser = functions.https.onCall((data, context) => {
return admin // <== See return here
.auth()
.createUser({
phoneNumber: data.phoneNumber,
displayName: data.displayName,
})
.then((user) => {
// store data in firestore
return admin // <== See return here
.firestore()
.collection('user')
.doc(user.uid)
.set({ ...data });
})
.then(() => {
// The set() method returns Promise<void>
// To send data back to the client, return data that can be JSON encoded
return { result: 'user created' };
})
.catch(error => {
// See https://firebase.google.com/docs/functions/callable#handle_errors
// on how to handle errors
});
});
This way, when your front-end gets back the Callable Cloud Function response, you are sure that the user and the Firestore document were created.
If you want to use the async/await
keywords, do as follows. Note that it is not recommended to mixup then()
with async/await
.
exports.createUser = functions
.https.onCall(async (data, context) => { // <== See async here
try {
const user = await admin.auth().createUser({
phoneNumber: data.phoneNumber,
displayName: data.displayName,
});
await admin.firestore().collection("user").doc(user.uid).set({ ...data });
return { result: 'user created' }
} catch (error) {
console.log(error); // If desired
// See https://firebase.google.com/docs/functions/callable#handle_errors
// on how to handle errors
}
});
See how the code is much more readable, as if it was synchronous code.