1

I have updated to Firebase v9 a few weeks ago and I have an issue when trying to connect my Firebase App to Firestore Emulator.

firebase.js (my VueJS plugin, where I setup Firebase) :

import { initializeApp, getApps } from "firebase/app"
import { getAuth, connectAuthEmulator, onAuthStateChanged } from "firebase/auth";
import { getFirestore, connectFirestoreEmulator } from "firebase/firestore"
import { getStorage, connectStorageEmulator } from "firebase/storage";
import { getFunctions, connectFunctionsEmulator } from 'firebase/functions';
import { isSupported, getAnalytics } from "firebase/analytics";

export default async ({ app }, inject) => {

  const firebaseConfig = {
    apiKey: process.env.FIREBASE_API_KEY,
    authDomain: process.env.FIREBASE_AUTH_DOMAIN,
    databaseURL: process.env.FIREBASE_DATABASE_URL,
    projectId: process.env.FIREBASE_PROJECT_ID,
    storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.FIREBASE_MESSAGING_SERVICE_ID,
    appId: process.env.FIREBASE_APP_ID,
    measurementId: process.env.FIREBASE_MEASUREMENT_ID,
  }
  // I've checked, the values of firebaseConfig are all set here.

  // This IF statement is here to avoid initializing the app several times
  const apps = getApps();
  let firebaseApp = null;
  if (!apps.length) {
    firebaseApp = initializeApp(firebaseConfig);
  }
  else {
    firebaseApp = apps[0];
  }

  // INIT AUTH
  const auth = getAuth();
  auth.languageCode = 'fr';
  onAuthStateChanged(auth, async authUser => {
    const claims = authUser ? (await authUser.getIdTokenResult(true)).claims : null;
    await app.store.dispatch('onAuthStateChanged', { authUser, claims });
  },
  (error) => {
    console.error("Firebase Auth onAuthStateChanged ERROR", error)
  });
  
  // Get other services
  const firestore = getFirestore(firebaseApp);
  const storage = getStorage(firebaseApp);
  const functions = getFunctions(firebaseApp, process.env.FIREBASE_REGION);

  // Setup analytics if supported
  let analytics = null;
  const analyticsSupported = await isSupported()
  if (analyticsSupported) {
    analytics = getAnalytics();
    analytics.automaticDataCollectionEnabled = false;
  }

  // Connecting to emulators
  if (process.client && process.env.APP_ENV === 'local') {
    console.log("LOCAL ENVIRONMENT, CONNECTING TO EMULATORS...");
    connectAuthEmulator(auth, "http://localhost:9099");
    connectFirestoreEmulator(firestore, 'localhost', 8080);
    connectStorageEmulator(storage, "localhost", 9199);
    connectFunctionsEmulator(functions, "localhost", 5001);
  }

  Inject firebase objects into my VueJS app
  const fire = { auth, firestore, storage, functions, analytics }
  inject('fire', fire);
}

Here is the error I get, caused by this line : connectFirestoreEmulator(firestore, 'localhost', 8080);

enter image description here

FirebaseError Firestore has already been started and its settings can no longer be changed. You can only modify settings before calling any other methods on a Firestore object.

I am not trying to modify Firestore object's settings property myself, so it has to be the method connectFirestoreEmulator.

The problem can be narrowed down to the following code :

import { initializeApp } from "firebase/app"
import { getFirestore, connectFirestoreEmulator } from "firebase/firestore"

export default async ({ app }, inject) => {

  const firebaseConfig = {
    apiKey: process.env.FIREBASE_API_KEY,
    authDomain: process.env.FIREBASE_AUTH_DOMAIN,
    databaseURL: process.env.FIREBASE_DATABASE_URL,
    projectId: process.env.FIREBASE_PROJECT_ID,
    storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.FIREBASE_MESSAGING_SERVICE_ID,
    appId: process.env.FIREBASE_APP_ID,
    measurementId: process.env.FIREBASE_MEASUREMENT_ID,
  }

  firebaseApp = initializeApp(firebaseConfig);
  const firestore = getFirestore(firebaseApp);
  if (process.env.APP_ENV === 'local') {
    connectFirestoreEmulator(firestore, 'localhost', 8080);
  }

  const fire = { auth, firestore, storage, functions, analytics };
  inject('fire', fire);
}

I've managed to avoid triggering the error by adding process.client so it doesn't connect to emulators on server-side (SSR) :

  if (process.client && process.env.APP_ENV === 'local') {

However when I add that, the emulators are not connected when code is executed server-side (SSR) on the first page load, and initial Firestore data is being read from the real Firebase App instead of the emulators.

Any idea what can be done to manage proper connection to Firestore emulator on SSR ?

Is this a Firebase bug ?

Versions I use :

  • In my App : Firebase JS SDK v9.6.9
  • Emulators : firebase-tools v10.4.0 for the emulators

What I've already read/tried :

1 Answers1

0

Reviewing Firebase JS SDK issues related, it seems that the issue is because the Firestore instance (which is initialized like this: firestore = getFirestore(firebaseApp)) is called after the emulator (connectFirestoreEmulator) has been started.

After calling the "connectFirestoreEmulator" method, "firestore" variable is being used in the constant variable "fire = { auth, firestore, storage, functions, analytics }"

If you use "const fire" before connecting to the emulator, the problem may be solved.

Here is a code example that might help you:

firebaseApp = initializeApp(firebaseConfig);

 const fire = { auth, firestore, storage, functions, analytics };

  const firestore = getFirestore(firebaseApp);
  if (process.env.APP_ENV === 'local') {
    connectFirestoreEmulator(firestore, 'localhost', 8080);
  }

As a reference, I used this github repository.

  • Thank you for your reply ! I was talking about the Firebase JS SDK and not Firebase Admin, which are a bit different. – Guillaume Prévost Mar 24 '22 at 13:29
  • Thanks for the update, but unfortunately this solution doesn't work out (I tried it just to be sure and still get the error). I don't think the assignation of `const fire = ...` is responsible : it seems to come from what happens INSIDE the call to `connectFirestoreEmulator` : whenever it's called on server-side I get the error. The issue still remains unsolved :( – Guillaume Prévost Mar 30 '22 at 15:26