Home > Software design >  Firestore: How do I update email and store it in my firestore collection as a single batch operation
Firestore: How do I update email and store it in my firestore collection as a single batch operation

Time:09-07

I am writing react native project, where users can sign in with their username and password. As firebase authentication does not have the functionality to sign in with username and password I do a bit of a trick. On registration I store user's email in my Users collection along with other useful users info. So when a user tries to sign in with username and password, I go to my Users collection, look for the respective username, and fetch the corresponding email address. So in disguise, I use firestore method .signInWithEmailAndPassword(email, password) . Not ideal, but it does the job.

However, the problem is when a user wants to update his/hers email address to a new one. Then, I surely want to update the email value within my users collection, so I can perform proper sign in. Here is the code to update user's email:

const onChangeEmail = async () => {
    await firebase
      .auth()
      .signInWithEmailAndPassword(email, password)
      .then(async function (userCredential) {
        await userCredential.user.updateEmail(newEmail).then(async () => {
          await updateEmailInFirestore(newEmail);
        });
      })
      .catch((error) => {
        console.log("Error while updating email: ", error);
      });
  };

Where updateEmailInFirestore does the following:

export const updateEmailInFirestore = async (newEmail) => {
  if (!newEmail) {
    return;
  }
  await firebase
    .firestore()
    .collection("Users")
    .doc(firebase.auth().currentUser.uid)
    .update({ email: newEmail })
    .then(() => console.log("email was updated in user collection: ", newEmail))
    .catch((error) => console.log("error while updating email: ", error));
};

The above code works fine and does the job. However, the problem I am having is the following scenario: what if userCredential.user.updateEmail manages to execute successfully, but then updateEmailInFirestore fails or throws an exception? Then the value, which I have in my database wont be consistent with the user's updated email address and my login will fail misserably.

Is there a way that I can execute both userCredential.user.updateEmail and updateEmailInFirestore as batch operation, so that either both succeed or both fail? I have written batch operations before but they are along the lines of const batch = firebase.firestore().batch(); and my first operation is not related to firestore, but to firebase authentication?

CodePudding user response:

There's nothing much you can do when it comes to ensuring both the promises are resolved or not. Firebase Auth and Firestore are 2 different products so there is no concept like batched write. You can use a Promise.all() and ask user to retry in case either of the promises fail as shown below.

const updateUserEmail = async () => {
  try {
    await Promise.all([
      user.updateEmail(newEmail),
      userDoc.update({
        email: newEmail
      })
    ])
  } catch (e) {
    console.log("Updating email failed")
    // prompt user to retry
  }
}

A better and widely used solution is to create an email of format [email protected] and use that with createUserWithEmailAndPassword(). When a user updates their username (if you want to allow that functionality), then you just have to update the email to [email protected]. You won't need Firestore for email if you use this method.

Do note that you won't be able to use functionalities like verify email as these email do not exist.

  • Related