firebase / firebase-js-sdk

Firebase Javascript SDK
https://firebase.google.com/docs/web/setup
Other
4.83k stars 892 forks source link

"Could not reach Cloud Firestore backend" after upgrading to v9 #5667

Closed Kojon74 closed 2 years ago

Kojon74 commented 2 years ago

[REQUIRED] Describe your environment

[REQUIRED] Describe the problem

I just started updating my code from Firebase v8 to v9 and then started running into this issue (which is weird because it was just working before I upgraded to v9 so I'm pretty sure it's not a network issue)

It seems like some firebase features do work, for eg the onAuthStateChange from firebase.auth, but whenever I try and run a Firestore command it seems to get stuck.

[2021-10-26T06:31:58.335Z]  @firebase/firestore:, Firestore (9.1.3): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
at node_modules/@firebase/util/dist/index.esm2017.js:1737:21 in stringLength
at node_modules/@firebase/util/dist/index.esm2017.js:1881:0 in <global>
at http://192.168.0.63:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:127402:19 in F
at node_modules/@firebase/firestore/dist/index.rn.js:10137:8 in <global>
at http://192.168.0.63:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:139150:48 in <unknown>
at node_modules/@firebase/firestore/dist/index.rn.js:10653:10 in <global>
at node_modules/@firebase/firestore/dist/index.rn.js:14006:24 in <global>
at node_modules/@firebase/firestore/dist/index.rn.js:14048:19 in cancel
at [native code]:null in flushedQueue
at [native code]:null in callFunctionReturnFlushedQueue

Relevant Code:

// Initialization
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import { getStorage } from "firebase/storage";

const firebaseApp = initializeApp({
// Initialization keys go here
});

const auth = getAuth();
const db = getFirestore();
const storage = getStorage();

export { auth, db, storage };

/////////////////////////////////////////////////////// IN A SEPARATE FILE ///////////////////////////////////////////////////////
import { onAuthStateChanged } from "firebase/auth";
import { auth, db } from "./firebase";
import {
  addDoc,
  collection,
  doc,
  getDoc,
  getDocs,
  limit,
  orderBy,
  query,
  updateDoc,
  where,
} from "firebase/firestore";

const SomeComponent = () => {
// other stuff
useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      (async () => {
        if (user) {
          let _userDoc;
          try {
            _userDoc = await getDocs(collection(db, "users"));
            // _userDoc = await getDoc(doc(db, "users", user.uid));
          } catch (e) {
            console.error(e);
          }
          console.log("bello");
          if (_userDoc.exists()) {
            setUserDoc(_userDoc);
            const userProdSnap = await getDocs(
              collection(_userDoc, "userProducts")
            );
            let curUserProducts = [];
            userProdSnap.forEach((doc) => curUserProds.push(doc.data()));
            setUserProducts(curUserProducts);
          } else {
            console.error("ERROR: user document doesn't exist");
          }
        }
      })();
    });
    return unsubscribe;
  }, []);
}
edi commented 2 years ago

The issue is caused by useFetchStreams being enabled by default, in the new modular build (v9). Since RN does not fully support fetch readable streams, projects have to fall back to XHR (which was the default in v8).

Therefore, please change your code accordingly:

import {initializeFirestore} from 'firebase/firestore'
const db = initializeFirestore(firebaseApp, {useFetchStreams: false})

There is a new PR #5643 by @schmidt-sebastian which is already merged which will automatically set it to false within RN projects.

EDIT: Side topic, if you ported your app to v9, in case you're using SMS authentication, just know that PhoneAuth methods are not properly exposed, so you'll get a few undefined methods, even though your IDE will properly show them as defined due their TS definitions. We're currently waiting on a fix for that as well (not sure if there's an issue opened either).

Feiyang1 commented 2 years ago

@edi Thanks for the comment! The fetch fix for RN should be released this week. For PhoneAuth in RN, we are going to update our documentation and the SDK itself to make it clear when some API is unavailable in certain environments (e.g. by throwing unsupported error, instead of returning an undefined).

edi commented 2 years ago

@Feiyang1 Oh, so you’re implying PhoneAuth will not be available as of v9 anymore? If so, that’s gonna be a big bummer.

Is there any reasoning behind making it unavailable for RN? We use PhoneAuth in multiple apps, and it works just fine so far.

Kojon74 commented 2 years ago

Thanks @edi, does that mean I should replace the getFirestore with initializeFirestore? I want to read more on initializeFirestore but I can't seem to find the docs on it, could you help point me in the right direction?

edi commented 2 years ago

Thanks @edi, does that mean I should replace the getFirestore with initializeFirestore?

Correct, change your import and your firestore (const db) line as per my example, then you’re good to go.

Feiyang1 commented 2 years ago

@edi IIUC, PhoneAuth relies on recaptcha to work, which wouldn't work in RN. The auth lib in v8 is monolithic, and provide a single bundle for all environments, while v9 provide different bundles for different environments, so we can define the API differently depending on what is supported in a specific environment.

I don't think PhoneAuth works in RN even with v8. Is it not the case?

angusryer commented 2 years ago

The issue is caused by useFetchStreams being enabled by default, in the new modular build (v9). Since RN does not fully support fetch readable streams, projects have to fall back to XHR (which was the default in v8).

Therefore, please change your code accordingly:

import {initializeFirestore} from 'firebase/firestore'
const db = initializeFirestore(firebaseApp, {useFetchStreams: false})

Brilliant, thank you. I'm using typescript. Is there an appropriate settings type to use for this config object?

I see the FirestoreSettings type is exposed in firebase/firestore but does not have the useFetchStreams property. Looks like PrivateSettings and its implementation in FirestoreSettingsImpl do (in @firebase/firestore/dist/firestore/src/lite-api/settings.d.ts), but it doesn't feel right importing those defs directly into my project.

edi commented 2 years ago

I don't think PhoneAuth works in RN even with v8. Is it not the case?

@Feiyang1 You're both right, and wrong. No (pure) RN user is probably going to post about this issue, but I understand where the confusion is coming from.

I alongside probably all users who noticed about this being an issue, are coming from Expo, which has it's own recaptcha module, which gives us the option to use PhoneAuth, without having to implement react-native-firebase which is a bit of an overhead to deal with.

So, to answer your question, yes, that is the case, we have been using PhoneAuth since 7.x within RN (via Expo, that is). Therefore, not exposing those methods anymore into RN just because it doesn't have recaptcha support out of the box, would be a big mistake, as there are about ~400K (public) packages, using expo, and ~2K weekly downloads just for the recaptcha verifier module itself.

We're currently standing by, waiting on @sam-gc to properly expose those within RN, so that we can properly publicly deploy our newly ported apps to v9.

edi commented 2 years ago

@angusryer You're right. As per the API Reference it seems that TS definitions do not include useFetchStreams.

On another hand, you may work around this, until #5643 makes it to the live build, by using the same initializeFirestore method with the experimentalForceLongPolling: true prop instead of useFetchStreams: false.

That should get you going for the time being, even though it's a little bit more intrusive.

SpyGuy0215 commented 2 years ago

That's so strange, I tried that and it didn't work Code:

 await console.log('Initializing Firebase App...')
    const firebaseApp = await initializeApp({
      apiKey: "xxxxxxxxxxx",
      authDomain: "xxxxxx",
      projectId: "xxxxxx",
    })
    console.log('App Initialized, Initializing Firestore...')
    db = await initializeFirestore(firebaseApp, {useFetchStreams: false, experimentalForceLongPolling:true})

this still throws the error. Why?

angusryer commented 2 years ago

@Shashank-Prasanna & @edi I hadn't replied back, but experimentalForceLongPolling: true did not work for me. I also tried experimentalAutoDetectLongPolling: true just in case. For context, I'm running solely on an iOS emulator at the moment.

For anyone using Typescript, you can extend the FirestoreSettings interface like so:

// `FirestoreSettings` interface extends `PrivateSettings` which contains `useFetchStreams`.
const firestoreSettings: FirestoreSettings & { useFetchStreams: boolean } = {
    useFetchStreams: false
};

export const fsdb = initializeFirestore(app, firestoreSettings);

And with that I'm no longer getting the original error.

SpyGuy0215 commented 2 years ago

@Shashank-Prasanna & @edi I hadn't replied back, but experimentalForceLongPolling: true did not work for me. I also tried experimentalAutoDetectLongPolling: true just in case. For context, I'm running solely on an iOS emulator at the moment.

I ended up extending the FirestoreSettings interface like so:

// `FirestoreSettings` itself extends `PrivateSettings` which contains `useFetchStreams`.
const firestoreSettings: FirestoreSettings & { useFetchStreams: boolean } = {
  useFetchStreams: false
};

export const fsdb = initializeFirestore(app, firestoreSettings);

And with that I'm no longer getting the original error.

I tried this but the original error comes up and I also get another error: Type annotations can only be used in TypeScript files

Here's my full code:

import { initializeApp } from 'firebase/app'
import { initializeFirestore } from 'firebase/firestore'

console.log('config.js')
const firebaseApp = initializeApp({
  apiKey: "xxxxxxxx",
  authDomain: "xxxxxxxx",
  projectId: "xxxxxxxx",
})

console.log('DB initialized')

const firestoreSettings: FirestoreSettings & { useFetchStreams: boolean} ={
  useFetchStreams: false,
}

const db = initializeFirestore(firebaseApp, firestoreSettings)

export default db

for context, I am using expo-cli with an iOS device on react native

The really odd thing is that when I try it out on my PC in chrome, it works! But when I try it out on iOS in the expo go app, it throws the error

angusryer commented 2 years ago

Right @Shashank-Prasanna you'll have to remove the type defs if you're not using Typescript:

const firestoreSettings = {
  useFetchStreams: false
}

When testing this using the web, I also do not get the error. I haven't tested on an Android device.

SpyGuy0215 commented 2 years ago

@angusryer The odd thing is that this works only in a web browser... when I use the React Native web app on my iOS phone (with expo and expo go), the error still arises. Perhaps it is related to this warning I always receive?

[2021-10-31T14:15:43.953Z]  @firebase/firestore: Firestore (9.2.0): Connection WebChannel transport errored: 
me {type: 'c', target: Y, g: Y, defaultPrevented: false, status: 1}
defaultPrevented: false
g: Y {s: false, o: undefined, i: kb, R: Y, J: null, …}
status: 1
target: Y {s: false, o: undefined, i: kb, R: Y, J: null, …}
type: "c"
[[Prototype]]: qc

I never receive this error in a web browser, yet I do on iOS (haven't tested on Android). I can't understand for the life of me why this happens!

schmidt-sebastian commented 2 years ago

useFetchStreams is now disabled for React Native if you install version 9.2.0. If this does not solve your issue, we can add this flag to the official type definitions, which should make it easier to use.