Firestore Error - Cannot read property `firestore` of null - firebase

I am trying to use firestore with my react project after authentication to write some data to firebase. But whenever doc.set is called, I get an error saying Cannot read property firestore of null.
Here is my firebase config file.
import firebase from 'firebase/app';
const config = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
};
if (firebase.apps.length === 0) {
console.log('Initializing firebase');
firebase.initializeApp(config);
}
export default firebase;
And here is my firebase utils file which I use to access the common requirements from firebase.
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import {FIREBASE_COLLECTIONS} from './constants';
export const getIdToken = async () => {
try {
const {currentUser} = firebase.auth();
if (currentUser) {
const idToken = await currentUser.getIdToken();
return idToken;
}
return currentUser;
} catch (error) {
throw error || new Error('Error retrieving ID token');
}
};
export const logout = async () => {
try {
await firebase.auth().signOut();
} catch (error) {
throw error || new Error('Error logging out');
}
};
export const loginUser = async (email: string, password: string) => {
try {
const user = await firebase
.auth()
.signInWithEmailAndPassword(email, password);
return user;
} catch (error) {
let message = '';
switch (error.code) {
case 'auth/invalid-email':
message = 'Invalid Email Id';
break;
case 'auth/user-disabled':
message = 'User is temporarily disabled';
break;
case 'auth/user-not-found':
message = 'User not found. Please register';
break;
case 'auth/wrong-password':
message = 'Incorrect password';
break;
default:
message = 'Error logging in';
break;
}
throw new Error(message);
}
};
export const registerUser = async (email: string, password: string) => {
try {
const user = await firebase
.auth()
.createUserWithEmailAndPassword(email, password);
return user;
} catch (error) {
const errorCode = error.code;
let message = '';
switch (errorCode) {
case 'auth/email-already-in-use':
message = 'Email already in use';
break;
case 'auth/invalid-email':
message = 'Invalid email ID';
break;
case 'auth/weak-password':
message = 'Weak password';
break;
default:
message = 'Error registering user';
break;
}
throw new Error(message);
}
};
export const signInWithGoogle = async () => {
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({prompt: 'select_account'});
const scopes = ['profile', 'email'];
scopes.forEach(scope => provider.addScope(scope));
try {
return await firebase.auth().signInWithPopup(provider);
} catch (error) {
let message = '';
switch (error.code) {
case 'auth/account-exists-with-different-credential':
message = 'Account exists with another sign in type';
break;
case 'auth/popup-closed-by-user':
message = 'Login popup closed';
break;
default:
message = 'Error signing in';
break;
}
throw new Error(message);
}
};
export const isLoggedIn = (): boolean => {
const {currentUser} = firebase.auth();
return currentUser !== null;
};
export const getUserDoc = (
id: string
): firebase.firestore.DocumentReference => {
return firebase
.firestore()
.collection(FIREBASE_COLLECTIONS.USERS)
.doc(id);
};
export default firebase;
I'm calling the getUserDoc function from the firebase utils and using it to write data to the user.
function* registerWithPassword(user: UserInput) {
try {
const userData: firebase.auth.UserCredential = yield call(
registerUser,
user.email,
user.password
);
if (userData === null || userData.user === null) {
yield put(
authFailureAction.failure({
register: 'Unable to register user',
})
);
} else {
const userDoc = getUserDoc(userData.user.uid);
yield call(
userDoc.set,
{labels: [], pinnedNotes: [], name: user.name},
{merge: true}
);
}
} catch (e) {
console.log({e});
yield put(
authFailureAction.failure({
register: e.message || 'Unable to register user',
})
);
}
}
When userDoc.set is called, the error occurs.
From some debugging, it looks like firebase is null. That's why firebase.firestore() throws an error.
But if firebase is null, auth shouldn't work too but I haven't had any problems with firebase auth.
Also note that I am importing firebaseConfig.js in my index.js file, so I'm sure that firebase has been initialized. This is also due to the fact that users are being registered but for some reason when I'm accessing firestore from firebase.firestore(), firebase is null.
You can access the project here. Try to register a user and you'll see the error. Since it depends on Firebase, you'll have to add a relevant .env file.

You have to use the firebase that has been initialized.
import firebase from 'firebase/app'; <-- This one hasn't been initialized.
...
firebase.initializeApp(firebaseConfig);
export default firebase; <-- This one is initialized.
In your firebase_config, add firestore, and auth
import firebase from 'firebase/app';
import 'firebase/auth'; <----- add this here.
import 'firebase/firestore'; <----- add this here.
const config = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
};
if (firebase.apps.length === 0) {
console.log('Initializing firebase');
firebase.initializeApp(config);
}
export default firebase;
Change the first 3 lines.
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import {FIREBASE_COLLECTIONS} from './constants';
export const getIdToken = async () => {
try {
const {currentUser} = firebase.auth();
if (currentUser) {
const idToken = await currentUser.getIdToken();
....
to
import firebase from './firebase_config.js'; // or wherever your firebase.initializeApp(...) live.
import {FIREBASE_COLLECTIONS} from './constants';
export const getIdToken = async () => {
try {
const {currentUser} = firebase.auth();
if (currentUser) {
const idToken = await currentUser.getIdToken();
....
Good day.

The error may be occurring because of the issue with the multiple firebase imports/exports done in files instead of a single module as a source.
The firebase module could be exported only in firebase_config file, and then be imported from there in other files.
So in firebase_utils, you can add instead of:
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import firebase from './firebase-config.js';
You can also remove export default firebase; from firebase_utils to prevent firebase namespace from getting overwritten.
So the final firebase_config.js file may look as:
// Firebase App (the core Firebase SDK)
var firebase = require("firebase/app");
// Add the Firebase products that you want to use
require("firebase/auth");
require("firebase/firestore");
// Your app's Firebase project configuration
var firebaseConfig = {
// ...
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
// Single source for all imports of the firebase module
export default firebase;
You can also take reference from this article and this GitHub repo for firebase importing examples.

Related

Service messaging is not available

I want to integrate FCM with nextjs project.
This error is occurring whenever I save the firebase.js config file. I'm not being able to use Firebase Cloud Messaging in Firebase V9.
I use firebase 9.10.0
my firebase.js config
import { initializeApp } from 'firebase/app';
import { getToken, getMessaging, onMessage } from 'firebase/messaging';
const firebaseConfig = {
apiKey: "*************",
authDomain: "********************",
projectId: "******************",
storageBucket: "*****************",
messagingSenderId: "*************",
appId: "**********************"
};
console.log('*** Environment ***', process.env.REACT_APP_ENV)
console.log('*** Firebase Config ***', firebaseConfig)
const firebaseApp = initializeApp(firebaseConfig);
const messaging = getMessaging(firebaseApp);
export const getOrRegisterServiceWorker = () => {
if ('serviceWorker' in navigator) {
return window.navigator.serviceWorker
.getRegistration('/firebase-push-notification-scope')
.then((serviceWorker) => {
if (serviceWorker) return serviceWorker;
return window.navigator.serviceWorker.register('/firebase-messaging-sw.js', {
scope: '/firebase-push-notification-scope',
});
});
}
throw new Error('The browser doesn`t support service worker.');
};
export const getFirebaseToken = () =>
getOrRegisterServiceWorker()
.then((serviceWorkerRegistration) =>
getToken(messaging, { vapidKey: "***********", serviceWorkerRegistration }));
export const onForegroundMessage = () =>
new Promise((resolve) => onMessage(messaging, (payload) => resolve(payload)));
Ather searing a lot I found solutions:
I change the code of my firebase.js file to the below code
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getMessaging, getToken } from "firebase/messaging";
import localforage from "localforage";
const firebaseConfig = {
apiKey: "**********************",
authDomain: "*******************",
projectId: "******************",
storageBucket: "****************",
messagingSenderId: "****************",
appId: "**************"
};
// Initialize Firebase
const firebaseCloudMessaging = {
init: async () => {
initializeApp(firebaseConfig);
try {
const messaging = getMessaging();
const tokenInLocalForage = await localStorage.getItem("fcm_token");
// Return the token if it is alredy in our local storage
if (tokenInLocalForage !== null) {
return tokenInLocalForage;
}
// Request the push notification permission from browser
const status = await Notification.requestPermission();
if (status && status === "granted") {
// Get new token from Firebase
const fcm_token = await getToken(messaging, {
vapidKey:
"********************",
});
console.log("token in fcm_token", fcm_token);
// Set token in our local storage
if (fcm_token) {
localforage.setItem("fcm_token", fcm_token);
return fcm_token;
}
}
} catch (error) {
console.error(error);
return null;
}
},
};
export { firebaseCloudMessaging };

Can we make firebase actions serializable for redux toolkit?

I am using react and redux toolkit in a project. I also use firebase to manage authentication for this project.
I'm dispatching an async thunk that I call login to login users. And here I am calling the signInWithEmailAndPassword method of firebase. I export this method from a file named firebase.ts. You can find code snippets below.
// firebase.ts
import { initializeApp } from "firebase/app";
import {
getAuth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
sendPasswordResetEmail,
sendEmailVerification,
updateEmail,
updatePassword,
reauthenticateWithCredential,
EmailAuthProvider,
} from "firebase/auth";
const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_FIREBASE_APP_ID,
measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
};
const firebaseApp = initializeApp(firebaseConfig);
export const auth = getAuth(firebaseApp);
export {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
sendPasswordResetEmail,
sendEmailVerification,
updateEmail,
updatePassword,
reauthenticateWithCredential,
EmailAuthProvider,
};
// UserContent/thunks.ts
import { createAsyncThunk } from "#reduxjs/toolkit";
import { auth, signInWithEmailAndPassword, signOut } from "#app/api/firebase";
import { UserLoginForm } from "#common/formTypes";
export const login = createAsyncThunk(
"userContent/login",
async (data: UserLoginForm) => {
const { email, password } = data;
const response = await signInWithEmailAndPassword(auth, email, password);
return response;
}
);
export const logout = createAsyncThunk("userContent/logout", async () => {
const response = await signOut(auth);
return response;
});
But as you can guess in the console, I get a warning like the following.
Console Warning Image
Of course I know I can turn off this warning very easily. But is there a better way to solve this?
You can convert your response to a serializable object by adding .toJson() to the response.
export const login = createAsyncThunk(
"userContent/login",
async (data: UserLoginForm) => {
const { email, password } = data;
const response = await signInWithEmailAndPassword(auth, email, password);
return response.toJSON();
}
);

Please be sure to call `initializeAuth` or `getAuth` before starting any other Firebase SDK. [Firebase Auth on NextJS App]

I'm trying to deploy my NextJS App on Vercel. But I'm getting this error.
Also, my project runs smoothly in development mode, but why am I getting such an error while deploying?
Firebase.ts File
import { initializeApp } from "firebase/app";
import {
getAuth,
signInWithEmailAndPassword,
onAuthStateChanged,
signOut,
} from "firebase/auth";
import { handlerSetUser } from "./utils";
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
onAuthStateChanged(auth, (user: any) => {
if (user) {
handlerSetUser(user);
} else {
handlerSetUser(false);
}
});
export const login = async (email: any, password: any) => {
try {
const result = await signInWithEmailAndPassword(auth, email, password);
return result;
} catch (error: any) {
alert(error.message);
}
};
export const logout = async () => {
try {
await signOut(auth);
} catch (error: any) {
alert(error.message);
}
};
Vercel Deployment Error
_errorFactory: ErrorFactory {
service: 'auth',
serviceName: 'Firebase',
errors: {
'dependent-sdk-initialized-before-auth': 'Another Firebase SDK was initialized and is trying to use Auth before Auth is initialized. Please be sure to call `initializeAuth` or `getAuth` before starting any other Firebase SDK.'
}
},

firebase v9 error could not reach cloud firestore backend nuxtjs

Firebase v9 is acting weird I think, I have on my code:
async nuxtServerInit({ commit }) {
try {
const dryers = await getDocs(collection(db, "dryers"))
const payload = dryers.docs.map(item => {
return {
docId: item.id,
...item.data()
}
})
commit("LOAD_DRYERS", payload)
} catch (err) {
console.error(err.message || "Could not process the request, something went wrong.")
}
}
This is part of my vuex actions that should trigger on init, it is working fine in this part, the code is populated to the state, but the problem is, whenever I load data on click event, i.e:
<button #click.prevent="testQuery">Test query</button>
...
async testQuery() {
try {
const x = await getDocs(collection(db, "dryers"))
console.log(x)
} catch (err) {
console.error(err)
}
}
the weird thing is, when testQuery is executed, I get a could not reach cloud firestore backend error response, when in fact, a successful query is made during init.
here's my config:
import { initializeApp } from 'firebase/app'
import { initializeAuth, getAuth } from "firebase/auth"
import { initializeFirestore } from "firebase/firestore"
const firebaseConfig = {
apiKey: process.env.apiKey,
authDomain: process.env.authDomain,
projectId: process.env.projectId,
storageBucket: process.env.storageBucket,
messagingSenderId: process.env.messagingSenderId,
appId: process.env.appId,
measurementId: process.env.measurementId
}
// Initialize Firebase
const app = initializeApp(firebaseConfig)
initializeAuth(app)
const auth = getAuth(app)
const db = initializeFirestore(app, {
experimentalForceLongPolling: true
})
export { auth, db }
I'm using firebase v9.1.2, any tips?

Phoneauthprovider is not a function firebase react-native

Hi everybody im making a app using react-native and fire base im have this initial config at firebase config :
import firebase from 'firebase/app';
import 'firebase/auth';
import Constants from 'expo-constants';
// Firebase Config
// Initialize Firebase
export 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
};
let Firebase
if (firebase.apps.length === 0) {
console.log('hello world')
Firebase = firebase.initializeApp(firebaseConfig);
}
export default Firebase;
And im triyng to call this method:
const loginUser = async() => {
switch(loginType){
case 0:
break;
case 1:
if (typeof(verificationId) == 'string') {
setLoading(true)
try {
const credential = new Firebase.auth.PhoneAuthProvider.credential(
verificationId,
verificationCode
);
await Firebase.auth.signInWithCredential(credential);
showMessage({ text: 'Phone authentication successful 👍' });
} catch (err) {
setLoading(false)
showMessage({ text: `Error: ${err.message}`, color: 'red' });
}
} else {
try {
const phoneProvider = Firebase.auth.PhoneAuthProvider();
const verificationId = await phoneProvider.verifyPhoneNumber(
phoneNumber,
recaptchaVerifier.current
);
setVerificationId(verificationId);
showMessage({
text: 'Verification code has been sent to your phone.',
});
} catch (err) {
showMessage({ text: `Error: ${err.message}`, color: 'red' });
}
}
break;
}
}
When im try to call my 'phone Login method' react-native show me this message:
im use this guide for how to configure the enviroment:
https://blog.jscrambler.com/how-to-integrate-firebase-authentication-with-an-expo-app
but using phone verification with recaptcha im not found the problem i believe the problem its in my implementation but in not found nothing
Thanks for the answers
I see you're trying to implement phone auth using firebase and I personally had success doing that using this:
async function signInWithPhoneNumber(phoneNumber) {
//1. Have the user input thei phone number into a TextInput and pass it to this function
//2. Have a confirm useState to make sure the verification code was sent successfully by firebase
//3. Check for the confirm state in the main component and show the user another TextInput to enter the verification code if confirm has a value
await firebase.auth()
.signInWithPhoneNumber(phoneNumber)
.then(confirmation => {
setConfirm(confirmation)
})
.catch(e => {
Alert.alert('Error sending verification code to this phone number')
})
}
async function confirmCode(code) {
//1. Have the code the user entered through the TextInput pass through here and call the below function
try {
let validation = await confirm?.confirm(code)
if (validation) console.log('correct code.')
} catch (error) {
Alert.alert('Invalid code.')
}
}
You're importing your own Firebase object, which is an instance of FirebaseApp. The PhoneAuthProvider class is not defined on FirebaseApp, but rather is in the (static) firebase.auth namespace.
So you either need to also import the regular Firebase Auth SDK into your code, instead of just your own Firebase object, or you can attach the firebase.authnamespace to yourFirebase` object and use it from there with:
...
if (firebase.apps.length === 0) {
console.log('hello world')
Firebase = firebase.initializeApp(firebaseConfig);
Firebase.auth = firebase.auth;
}
export default Firebase;

Resources