title: How to await the creation of a document created in cloud functions with onSnapshot
body: <p>I want to securely create a <code>user</code> document <code>onCreate</code> that is in sync with the <code>auth.user</code> database in Firebase v9.
  I think it wouldn't be secure to let a registered user create a user document. So I wrote a cloud function which triggers on <code>functions.auth.user().onCreate()</code> and creates a user document.</p>
  <p><strong>Problem:</strong></p>
  <p>I have the problem keeping them in sync as the <code>onSnapshot</code>method which should await for the user document to exists already returns a promise if the user document does not yet exists. Sometimes it works and sometimes not. So I don't know when I can update the by the cloud function created <code>user</code> document.</p>
  <p><strong>Question:</strong></p>
  <p>Why does the onSnapshot sometimes work and sometimes not. How can I fix it?</p>
  <p>Here is a link to a helpful Article which seem to doesn't work in v9. <a href="https://medium.com/firebase-developers/patterns-for-security-with-firebase-offload-client-work-to-cloud-functions-7c420710f07" rel="nofollow noreferrer">Link</a></p>
  <p>I tried and searched everywhere. I can't believe this is not a standard feature and still a requested topic. This seems so basic.</p>
  <h3>Error</h3>
  <p><code>error FirebaseError: No document to update:</code> as <code>const user = await createAccount(displayName, email, password);</code> returns even if user is not yet in doc.data()</p>
  <h3>Sign Up function</h3>
  <pre class="lang-js prettyprint-override"><code>interface SignUpFormValues {
      email: string;
      password: string;
      confirm: string;
      firstName: string;
      lastName: string;
    }
  
  const createAccount = async (
      displayName: string,
      email: string,
      password: string
    ) =&gt; {
      // Create auth user
      const userCredential = await createUserWithEmailAndPassword(
        auth,
        email,
        password
      );
      // -&gt; Signed in
  
      // Update Profile
      const user = userCredential.user;
      const uid = user.uid;
      await updateProfile(user, {
        displayName: displayName,
      });
  
      // IMPORTANT: Force refresh regardless of token expiration
      // auth.currentUser.getIdToken(true); // -&gt; will stop the onSnapshot function from resolving properly
  
      // Build a reference to their per-user document
      const userDocRef = doc(db, &quot;users&quot;, uid);
  
      return new Promise((resolve, reject) =&gt; {
        const unsubscribe = onSnapshot(userDocRef, {
          next: (doc) =&gt; {
            unsubscribe();
            console.log(&quot;doc&quot;, doc); // -&gt; returning undefined
            console.log(&quot;doc.data()&quot;, doc.data()); // -&gt; returning undefined
            resolve(user); // -&gt; returning undefined
          },
          error: (error) =&gt; {
            unsubscribe();
            console.log(&quot;error&quot;, error);
            reject(error);
          },
        });
      });
    };
  
  const handleSignUp = async (values: SignUpFormValues) =&gt; {
      const { firstName, lastName, email, password } = values;
      const displayName = `${firstName} ${lastName}`;
  
      try {
        setError(&quot;&quot;);
        setLoading(true);
  
        // Create user account
        const user = await createAccount(displayName, email, password);
        console.log(&quot;createAccount -&gt; return:&quot;, user); // -&gt; problem here sometimes return undefined
  
        // Update user
        const newUserData = {
          displayName: displayName,
          firstName,
          lastName,
        };
        // Build a reference to their per-user document
        const userDocRef = doc(db, &quot;users&quot;, user.uid);
        await updateDoc(userDocRef, newUserData);
  
        // Send Email verification
        await authSendEmailVerification(user);
  
        // Logout
        await logout();
  
        navigate(&quot;/sign-up/email-verification&quot;, { state: values });
      } catch (error: any) {
        const errorCode = error.code;
        const errorMessage = error.message;
        console.log(&quot;error&quot;, error);
        console.log(&quot;error&quot;, errorCode);
        if (errorCode === &quot;auth/email-already-in-use&quot;) {
          const errorMessage =
            &quot;Failed to create an account. E-Mail address is already registered.&quot;;
          setError(errorMessage);
          console.log(&quot;error&quot;, errorMessage);
        } else {
          setError(&quot;Failed to create account.&quot;);
        }
      }
      setLoading(false);
    };
  </code></pre>
  <h3>Cloud function which triggers the user onCreate</h3>
  <pre class="lang-js prettyprint-override"><code>// On auth user create
  export const authUserWriteListener = functions.auth
    .user()
    .onCreate(async (user, context) =&gt; {
      console.log(&quot;user:&quot;, user);
      const userRef = db.doc(`users/${user.uid}`);
      await userRef.set({
        email: user.email,
        createdAt: context.timestamp,
        firstTimeLogin: true,
      });
  
      return db.doc(&quot;stats/users&quot;).update({
        totalDocsCount: FieldValue.increment(1),
      });
    });
  </code></pre>