undefined is not an object (evaluating '_app.default.apps') - firebase

this is my code. I am new to react native and trying to make a firebase authanticator using firebase in expo. please hell me with this error. I am facing similar issue for other things related to firebase also.
import firebase from "firebase/app";
import "firebase/auth";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "removed",
authDomain: "removed",
databaseURL: "removed",
projectId: "removed",
storageBucket: "removed",
messagingSenderId: "removed",
appId: "removed"
};
// Initialize Firebase
let app;
if(firebase.apps.length === 0){
app = firebase.initializeApp(firebaseConfig);
}else{
app = firebase.app()
}
const auth = firebase.auth
export { auth };```

see How to check if a Firebase App is already initialized on Android
and switch to s/t like
import { initializeApp, getApps, getApp } from "firebase/app";
getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();

Related

Firebase App named '[DEFAULT]' already exists with different options or config

I'm building an app with NextJS, NextAuth and Firebase.
While implementing NextAuth, I've encountered this error:
error - FirebaseError: Firebase: Firebase App named '[DEFAULT]' already exists with different options or config (app/duplicate-app).
Here's my code:
[...NextAuth].js
import NextAuth from "next-auth/next";
import GoogleProvider from "next-auth/providers/google";
import { FirestoreAdapter } from "#next-auth/firebase-adapter";
import { db } from "#/firebase/config";
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
],
adapter: FirestoreAdapter(db),
});
My firebase config file
import { initializeApp, getApp, getApps } from "firebase/app";
import "firebase/auth";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: ___,
authDomain: ___,
projectId: ___,
storageBucket: ___,
messagingSenderId: ___,
appId: ___,
measurementId: ___,
};
const app =
getApps().length === 0
? initializeApp({ ...firebaseConfig, projectId: firebaseConfig?.projectId })
: getApp();
const db = getFirestore(app);
export { app, db };
As you can see in my config file, I'm testing if an app already exists, but it doesn't seem to work.
I've checked if somebody already had the same problem as me, but I didn't find an answer.
Any idea?
Thanks a lot,
Gabriel
According to the documentation for the Firebase Adapter for NextAuth.js, you should be passing in the configuration object to the adapter directly or an instance of Firestore from the Admin SDK (i.e. using import { getFirestore } from "firebase-admin/firestore").
Initially, you should try removing your "#/firebase/config" import and just use the configuration directly.
import NextAuth from "next-auth/next";
import GoogleProvider from "next-auth/providers/google";
import { FirestoreAdapter } from "#next-auth/firebase-adapter";
const firebaseConfig = {
apiKey: ___,
authDomain: ___,
projectId: ___,
storageBucket: ___,
messagingSenderId: ___,
appId: ___,
measurementId: ___,
};
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
],
adapter: FirestoreAdapter(firebaseConfig),
});
This initialization behaviour is defined in the main constructor in src/index.ts and in the initialization utility method in src/utils.ts.
The next step to try would be to make sure your Next.js sources make use of the Firebase Admin SDK instead of the client-side Firebase SDK which behaves differently.
If the above doesn't work, you can look for the more general cause. Sift through your codebase and look for statements that initialize Firebase service providers before your code pulls in "#/firebase/config". Any call to getApp() without any arguments will silently initialize the default FirebaseApp instance.
// NOTE: this is pseudo-code, not the actual implementation
// gets the named/default app, throwing an error if not initialized
export function getApp(name: string = DEFAULT_ENTRY_NAME): FirebaseApp {
const app = _apps.get(name);
if (!app && name === DEFAULT_ENTRY_NAME) return initializeApp(); // <-- this initializeApp is your problem
if (!app) throw new Error(name + " not initialized");
return app;
}
This also applies to calls that initialize a service (e.g. getFirestore()) without any app argument as they also will call getApp() internally.
// NOTE: this is pseudo-code, not the actual implementation
export function getFirestore(app?: FirebaseApp) {
app = app || getApp(); // use given app or use default
return app._providers.get('firestore') || initializeFirestore(app, DEFAULT_SETTINGS)
}
Unfortunately, tracking down this particular problem can be a pain as you module bundler/build tool might be "tree-shaking" the code and stripping what it thinks is unnecessary - which may include your getApp() and getFirestore() calls if you don't use app or db in the local code. Using just import "#/firebase/config" in this case should solve that.

Snack expo firebase/firestore error react native

Trying to run a react-native app (works locally!) on snack expo, but comes up with various error messages.
The first issue I think that snack expo is not ready using firebase sdk v9+, so I changed it from "firebase": "^9.16.0" to "firebase": "8.2.3" as someone suggested here.
But it means I have to change the firbase config file accordingly but I couldnt find much source to go on, so i probably made a few mistakes. This also comes up with and error and im not sure if what step I need to take next.
firebase config (^9.16.0)
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import Constants from 'expo-constants';
const firebaseConfig = {
apiKey: Constants.manifest.extra.apiKey,
authDomain: Constants.manifest.extra.authDomain,
projectId: Constants.manifest.extra.projectId,
storageBucket: Constants.manifest.extra.storageBucket,
messagingSenderId: Constants.manifest.extra.messagingSenderId,
appId: Constants.manifest.extra.appId
};
initializeApp(firebaseConfig);
export const auth = getAuth();
export const database = getFirestore();
firebase config (^8.2.3)
import firebase from "firebase/compat/app";
import "firebase/compat/auth";
import Constants from 'expo-constants';
const firebaseConfig = {
apiKey: Constants.manifest.extra.apiKey,
authDomain: Constants.manifest.extra.authDomain,
projectId: Constants.manifest.extra.projectId,
storageBucket: Constants.manifest.extra.storageBucket,
messagingSenderId: Constants.manifest.extra.messagingSenderId,
appId: Constants.manifest.extra.appId
};
initializeApp(firebaseConfig);
export const auth = getAuth();
export const database = getFirestore();
You are welcome to have a look at it on snack.expo.dev/#andrase/helfen

"Firebase Error : Firestore has already been started and its settings can no longer be changed." connecting Firebase v9 with Firestore Emulator

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);
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 :
https://firebase.google.com/docs/reference/js/app.md#initializeapp
Firebase Firestore emulator error `Host has been set in both settings() and useEmulator(), emulator host will be used`
FirebaseFirestore has already been started and its settings can no longer be changed
It's been a while, but I ran into a similar issue, and after a lot of hairpulling, I ended up with a solution (though it feels a little hacky).
Before running the connectFirestoreEmulator line, check if firestor._settingsFrozen is false. So you only run that line basically if Firestore hasn't already been initialized. You can check that firestore is getting initialized with the emulator settings by logging out the firestore variable before the connectFirestoreEmulator line and seeing what the settings say there--if it says port is 8080 and host is localhost, then you're good.
Here's my code for comparison (slightly different setup from yours but I believe we were running into the same issue):
import { initializeApp } from 'firebase/app';
import { connectAuthEmulator, getAuth } from 'firebase/auth';
import { connectFirestoreEmulator, getFirestore } from 'firebase/firestore';
const firebaseConfig = {
apiKey: "XXXXXXXXX",
authDomain: "XXXXXXXXX",
projectId: "XXXXXXXXX",
storageBucket: "XXXXXXXXX",
messagingSenderId: "XXXXXXXXX",
appId: "XXXXXXXXX",
measurementId: "XXXXXXXXX",
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);
export const db = getFirestore(app);
export default (context) => {
if (context.env.appEnv === 'dev') {
connectAuthEmulator(auth, `http://127.0.0.1:${context.env.authPort}`);
if (!db._settingsFrozen) {
connectFirestoreEmulator(db, '127.0.0.1', parseInt(context.env.firestorePort));
}
}
}
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.
You can try checking the setting.host value of your firebase object in order to check if it is already 'localhost', so you can skip calling the connectFirestoreEmulator() function.
This did happen to me in an Angular application using Hot Module Replacement. I tried to use a global constant, but did not work.
In my case, I'm using AngularFire (https://github.com/angular/angularfire), so I had to do something like this:
// ...
const firestore = getFirestore();
const host = (firestore.toJSON() as { settings?: { host?: string } }).settings?.host ?? '';
// console.log({ host });
if (process.env.APP_ENV === 'local' && !host.startsWith('localhost')) {
connectFirestoreEmulator(firestore, 'localhost', 8080);
}
// ...
In my case I had to use firestore.toJSON() in order to access the settings property, check how it is in your case.

Error importing firebase to React Native Expo

I want to authorize the FireBase application in React Native Expo, but I have an error.
I am using FireBase version 8.2.34 ( installed with the command "npm install firebase#8.2.3" ).
Code ( firebase.js ) :
// Import the functions you need from the SDKs you need
import * as firebase from "firebase";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
const firebaseConfig = { ... };
// Initialize Firebase
let app;
if (firebase.apps.length === 0) {
app = firebase.initializeApp(firebaseConfig);
} else {
app = firebase.app()
}
ExpoGO App error ( in line №2 ):
Uncaught Error
Can't find variable: IDBIndex
...
<global>
...\firebase.js:2
...
UPD:
Also, I have a problem with importing FireStore.
Note:use compat while importing.
i suggest to have these in one seprate file. (use web configuration)
import firebase from 'firebase/compat/app';
const firebaseConfig = {
apiKey: "",
authDomain: "",
databaseURL: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
};
const app = firebase.initializeApp(firebaseConfig);
export const db = app.firestore();

Using with Firebase / Expo query to Cloud Firestore

So I am trying to keep my code clean and build different files for querying... So I may be taking this harder than it needs to be.
I building a react-native app using Expo CLI.
I have created 3 files, one is my firebase config file,
one is a query file
then the actual file that is using that query.
it looks ok to me... but I get this error.
TypeError: undefined is not an object(evaluating'_firebase.firebase.firestore')
Here is my config file
import * as firebase from "firebase";
import 'firebase/firestore';
const firebaseConfig = {
apiKey: "api-key",
authDomain: "project-id.firebaseapp.com",
databaseURL: "https://project-id.firebaseio.com",
projectId: "project-id",
storageBucket: "project-id.appspot.com",
messagingSenderId: "sender-id",
appId: "app-id",
measurementId: "G-measurement-id"
};
firebase.initializeApp(firebaseConfig);
Then I have a a query file, basically acting as the API layer
import { firebase } from "./firebase";
const db = firebase.firestore();
const getListings = () => {
db.collection("listings").get();
};
export default {
getListings,
};
Then I am trying to view the queried data.
import listingApi from "../api/listings";
function ListingsScreen({ navigation }) {
const [listings, setListings] = useState([]);
useEffect(() => {
loadListings();
}, []);
const loadListings = async () => {
const response = await listingApi.getListings();
setListings(response.data);
};
This is my first time ever using Firebase or cloud firestore... so im really confused.
The error message is telling you that '_firebase.firebase.firestore' data is returning as undefined, this means the document you requested doesn't exist.
At the officiald documentation of Expo, is recommended to put the firebase config information in the same file with your code in order to use firebase, for example:
import * as firebase from 'firebase'
import 'firebase/firestore';
const firebaseConfig = { ... } // apiKey, authDomain, etc. (see above)
firebase.initializeApp(firebaseConfig);
const dbh = firebase.firestore();
dbh.collection("characters").doc("mario").set({
employment: "plumber",
outfitColor: "red",
specialAttack: "fireball"
})

Resources