Home > Mobile >  Firebase emulator setup: getFirestore() or getFirestore(firebaseApp)?
Firebase emulator setup: getFirestore() or getFirestore(firebaseApp)?

Time:03-05

When researching how to connect your app to the Firebase emulators (e.g., the Firestore emulator), I found the main documentation stating that we'd do it like this (Web version 9):

import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";

// firebaseApps previously initialized using initializeApp()
const db = getFirestore(); // <-- Notice they don't pass-in firebaseApp here
connectFirestoreEmulator(db, 'localhost', 8080); 

I've also seen the getFirestore() call done like this:

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

const config = { /* Firebase project config here  */ };
const firebaseApp = initializeApp(config);
const db = getFirestore(firebaseApp); // <-- Notice firebaseApp passed-in
connectFirestoreEmulator(db, 'localhost', 8080);

The Firestore API docs for getFirestore() states that it:

"Returns the existing Firestore instance that is associated with the provided FirebaseApp. If no instance exists, initializes a new instance with default settings."

I'm confused about whether or not to pass-in my initialized firebaseApp when calling getFirestore() based on that description. I have multiple Firebase services that I want to emulate (and have talk to each other) so I would think that I should pass-in my firebaseApp.

Which is correct? Are there "gotchas" to be aware of?

CodePudding user response:

I went snooping through the source code to see exactly what was going on and here's what I found:

The code for the getFirestore method is as follows:

/**
 * Returns the existing {@link Firestore} instance that is associated with the
 * provided {@link @firebase/app#FirebaseApp}. If no instance exists, initializes a new
 * instance with default settings.
 *
 * @param app - The {@link @firebase/app#FirebaseApp} instance that the returned {@link Firestore}
 * instance is associated with.
 * @returns The {@link Firestore} instance of the provided app.
 */
function getFirestore(app$1 = app.getApp()) {
    return app._getProvider(app$1, 'firestore').getImmediate();
}

If you don't pass an app instance to the function then it sets the app variable to app.getApp().

Earlier in the code, app is defined as require("@firebase/app") and so we need to look at the getApp method in that package.

The getApp method declaration is as-follows:

/**
 * Retrieves a {@link @firebase/app#FirebaseApp} instance.
 *
 * When called with no arguments, the default app is returned. When an app name
 * is provided, the app corresponding to that name is returned.
 *
 * An exception is thrown if the app being retrieved has not yet been
 * initialized.
 *
 * @example
 * ```javascript
 * // Return the default app
 * const app = getApp();
 * ```
 *
 * @example
 * ```javascript
 * // Return a named app
 * const otherApp = getApp("otherApp");
 * ```
 *
 * @param name - Optional name of the app to return. If no name is
 *   provided, the default is `"[DEFAULT]"`.
 *
 * @returns The app corresponding to the provided app name.
 *   If no app name is provided, the default app is returned.
 *
 * @public
 */
export declare function getApp(name?: string): FirebaseApp;

What this tells us is that when you initialize a firebase app, it gets initialized with a name that identifies the instance.

The initializeApp method declaration is below:

/**
 * Creates and initializes a {@link @firebase/app#FirebaseApp} instance.
 *
 * See
 * {@link
 *   https://firebase.google.com/docs/web/setup#add_firebase_to_your_app
 *   | Add Firebase to your app} and
 * {@link
 *   https://firebase.google.com/docs/web/setup#multiple-projects
 *   | Initialize multiple projects} for detailed documentation.
 *
 * @example
 * ```javascript
 *
 * // Initialize default app
 * // Retrieve your own options values by adding a web app on
 * // https://console.firebase.google.com
 * initializeApp({
 *   apiKey: "AIza....",                             // Auth / General Use
 *   authDomain: "YOUR_APP.firebaseapp.com",         // Auth with popup/redirect
 *   databaseURL: "https://YOUR_APP.firebaseio.com", // Realtime Database
 *   storageBucket: "YOUR_APP.appspot.com",          // Storage
 *   messagingSenderId: "123456789"                  // Cloud Messaging
 * });
 * ```
 *
 * @example
 * ```javascript
 *
 * // Initialize another app
 * const otherApp = initializeApp({
 *   databaseURL: "https://<OTHER_DATABASE_NAME>.firebaseio.com",
 *   storageBucket: "<OTHER_STORAGE_BUCKET>.appspot.com"
 * }, "otherApp");
 * ```
 *
 * @param options - Options to configure the app's services.
 * @param name - Optional name of the app to initialize. If no name
 *   is provided, the default is `"[DEFAULT]"`.
 *
 * @returns The initialized app.
 *
 * @public
 */
export declare function initializeApp(options: FirebaseOptions, name?: string): FirebaseApp;

And that tells us that if you call initializeApp without providing a name for the instance as the second parameter then the instance will be generated with the name "[DEFAULT]"

Ultimately this means that unless you're manually naming your firebase app instance you don't need to pass it in when you call getFirestore. It will be able to fetch your single primary app instance and use it where necessary.

There shouldn't be any difference between passing the app instance to getFirestore manually v.s. letting Firestore get the instance directly through the firebase/app package

CodePudding user response:

If you only need to use a single Firebase app you can do either one, but if you have multiple apps, you need to initialize each of them giving them their configs and getting their specific firestore.

import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";

const db = getFirestore();
connectFirestoreEmulator(db, 'localhost', 8080); 

Only works if you have an already initialized firebase app in your context and that there is only one.

If you wish to have more than one firebase app you need initialize each of them and specify what firestore you are trying to access.

By doing

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

const config = { /* Firebase project config here  */ };
const firebaseApp = initializeApp(config);
const db = getFirestore(firebaseApp);
connectFirestoreEmulator(db, 'localhost', 8080);
  • Related