QUESTION: How to use with the emulator
Hi, is this possible to use this data provider with the Firebase Emulator for firestore and firebase?
It should be possible to pass it into the dataprovider. Here's how I would do it, as per the firebase instructions.
import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";
import { FirebaseDataProvider } from 'react-admin-firebase';
const db = getFirestore();
connectFirestoreEmulator(db, 'localhost', 8080);
const dataProvider = FirebaseDataProvider(firebaseConfig, {
app: db.app,
// ^ get the app instance from the firestore instance
});
Let me know if there's any issues.
I get an error when calling it like that:
Uncaught TypeError: this.app.firestore is not a function
for anyone struggling with this. you need to import the "compat" sdk version
import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import { FirebaseDataProvider } from 'react-admin-firebase';
const db = firebase.firestore()
firebase.firestore().useEmulator('localhost', 8080);
const dataProvider = FirebaseDataProvider(firebaseConfig, {
app: db.app,
// ^ get the app instance from the firestore instance
});
@MarcoDaniele Do you have an example of how to use both the firestore and auth emulators? My App.tsx is below and am not clear how to integrate to use it with this? Thanks
import { Admin, Resource } from 'react-admin';
import { GigList, GigCreate, GigEdit } from './Gigs/Gigs';
import {
FirebaseAuthProvider,
FirebaseDataProvider,
RAFirebaseOptions,
} from 'react-admin-firebase';
import CustomLogin from './CustomLogin/CustomLogin';
import EventNoteIcon from '@mui/icons-material/EventNote';
const config = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_AUTH_DOMAIN,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_APP_ID,
measurementId: process.env.REACT_APP_MEASUREMENT_ID,
};
const options: RAFirebaseOptions = {
watch: ['Gigs'],
lazyLoading: {
enabled: true,
},
};
const dataProvider = FirebaseDataProvider(config, options);
const authProvider = FirebaseAuthProvider(config, options);
function App() {
return (
<Admin dataProvider={dataProvider} authProvider={authProvider} loginPage={CustomLogin}>
<Resource name="Gigs" list={GigList} create={GigCreate} edit={GigEdit} icon={EventNoteIcon} />
</Admin>
);
}
export default App;
Hello @SKempin, I just have this in my App.tsx
import firebase from 'firebase/compat/app';
import "firebase/compat/firestore"
import "firebase/compat/auth"
firebase.initializeApp(config)
if (window.location.hostname === 'localhost') {
firebase.firestore().useEmulator('localhost', 8080);
firebase.auth().useEmulator('http://localhost:9099/');
}
edit: you need a previously initialized firebase app using firebase.initializeApp(). edit: you also need to import "firebase/compat/firestore" and "firebase/compat/auth"
@MarcoDaniele Thanks, I have adjusted as below but am getting the same error @rzmz is experiencing:
import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import { Admin, Resource } from 'react-admin';
import { GigList, GigCreate, GigEdit } from './Gigs/Gigs';
import {
FirebaseAuthProvider,
FirebaseDataProvider,
RAFirebaseOptions,
} from 'react-admin-firebase';
import CustomLogin from './CustomLogin/CustomLogin';
import EventNoteIcon from '@mui/icons-material/EventNote';
const config = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_AUTH_DOMAIN,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_APP_ID,
measurementId: process.env.REACT_APP_MEASUREMENT_ID,
};
const options: RAFirebaseOptions = {
watch: ['Gigs'],
// logging: true,
lazyLoading: {
enabled: true,
},
};
firebase.initializeApp(config);
if (window.location.hostname === 'localhost') {
firebase.firestore().useEmulator('127.0.0.1', 8080);
firebase.auth().useEmulator('http://127.0.0.1:9099');
}
const dataProvider = FirebaseDataProvider(config, options);
const authProvider = FirebaseAuthProvider(config, options);
function App() {
return (
<Admin dataProvider={dataProvider} authProvider={authProvider} loginPage={CustomLogin}>
<Resource name="Gigs" list={GigList} create={GigCreate} edit={GigEdit} icon={EventNoteIcon} />
</Admin>
);
}
export default App;
which results in:
Uncaught runtime errors:
×
ERROR
firebase_compat_app__WEBPACK_IMPORTED_MODULE_0__.default.auth is not a function
TypeError: firebase_compat_app__WEBPACK_IMPORTED_MODULE_0__.default.auth is not a function
at ./src/App.tsx (http://localhost:3000/static/js/bundle.js:55:63)
at options.factory (http://localhost:3000/static/js/bundle.js:232328:31)
at __webpack_require__ (http://localhost:3000/static/js/bundle.js:231751:33)
at fn (http://localhost:3000/static/js/bundle.js:231985:21)
at ./src/index.tsx (http://localhost:3000/static/js/bundle.js:603:62)
at options.factory (http://localhost:3000/static/js/bundle.js:232328:31)
at __webpack_require__ (http://localhost:3000/static/js/bundle.js:231751:33)
at http://localhost:3000/static/js/bundle.js:232969:37
at http://localhost:3000/static/js/bundle.js:232971:12
this is using
"firebase": "^10.1.0",`
"react-admin": "^4.12.3"
"react-admin-firebase": "^4.1.2",
What lib versions are you using? Thanks
Hello @SKempin, you also need to import import "firebase/compat/firestore" (see edit to my previous post), sorry for my incomplete answer.
these are the versions i'm using:
"firebase": "^9.6.4",
"react": "^18.2.0",
"react-admin": "^4.14.3",
"react-admin-firebase": "^4.1.3",
@MarcoDaniele No problems, I have imported that at the top of the file. I think the issue may be with using Firebase 10, whereas you are on 9. Will try with your lib versions.
also on the latest version of firebase, here is my init:
import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import 'firebase/compat/storage';
import 'firebase/compat/auth';
import initializeApp = firebase.initializeApp;
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_APIKEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTHDOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECTID,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGEBUCKET,
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGINGSENDERID,
appId: import.meta.env.VITE_FIREBASE_APPID,
};
const options: any = {
renameMetaFields: {
created_at: 'fetched', // default: 'createdate'
updated_at: 'updated', // default: 'lastupdate'
},
transformToDb: toDb
};
const authOptions: any = {};
initializeApp(firebaseConfig);
if (import.meta.env.DEV) {
const db = firebase.firestore();
db.useEmulator("127.0.0.1", 8080);
options.app = db.app;
const auth = firebase.auth();
auth.useEmulator('http://127.0.0.1:9099');
authOptions.app = auth.app;
}
const dataProvider = FirebaseDataProvider(firebaseConfig, options);
const authProvider = FirebaseAuthProvider(firebaseConfig, authOptions);
the auth works, however accessing firestore does not, i am getting this error:
FirebaseError: [code=invalid-argument]: Expected f…ference, a DocumentReference or FirebaseFirestore
the message stays the same even if i put wrong host/port for the db emulator... so i guess it's just ignoring it. However, the auth doesn't work if i comment out the firestore part, seems like it fails to store it's session in that case.
so, i am looking for a solution as well.
NOTE: the firebase emulator is running on my api project, it's separated from the react-admin project, but i run them on the same computer ofc.
I didn't get this working in the end and have moved to https://firecms.co/ as I found this to be a better solution for my requirements.
also on the latest version of firebase, here is my init:
import firebase from 'firebase/compat/app'; import 'firebase/compat/firestore'; import 'firebase/compat/storage'; import 'firebase/compat/auth'; import initializeApp = firebase.initializeApp; const firebaseConfig = { apiKey: import.meta.env.VITE_FIREBASE_APIKEY, authDomain: import.meta.env.VITE_FIREBASE_AUTHDOMAIN, projectId: import.meta.env.VITE_FIREBASE_PROJECTID, storageBucket: import.meta.env.VITE_FIREBASE_STORAGEBUCKET, messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGINGSENDERID, appId: import.meta.env.VITE_FIREBASE_APPID, }; const options: any = { renameMetaFields: { created_at: 'fetched', // default: 'createdate' updated_at: 'updated', // default: 'lastupdate' }, transformToDb: toDb }; const authOptions: any = {}; initializeApp(firebaseConfig); if (import.meta.env.DEV) { const db = firebase.firestore(); db.useEmulator("127.0.0.1", 8080); options.app = db.app; const auth = firebase.auth(); auth.useEmulator('http://127.0.0.1:9099'); authOptions.app = auth.app; } const dataProvider = FirebaseDataProvider(firebaseConfig, options); const authProvider = FirebaseAuthProvider(firebaseConfig, authOptions);the auth works, however accessing firestore does not, i am getting this error:
FirebaseError: [code=invalid-argument]: Expected f…ference, a DocumentReference or FirebaseFirestorethe message stays the same even if i put wrong host/port for the db emulator... so i guess it's just ignoring it. However, the auth doesn't work if i comment out the firestore part, seems like it fails to store it's session in that case.
so, i am looking for a solution as well.
NOTE: the firebase emulator is running on my api project, it's separated from the react-admin project, but i run them on the same computer ofc.
I'm getting the same error. Supporting emulators is very important, especially while prototyping or developing using the free tier of Firebase...
Emulator support would be great. I had a quick look at the code and I came up against two issues;
- ./src/providers/database/firebase/FirebaseWrapper.ts constructor This wrapper takes the app passed in RAFirebaseOptions and instantiates firestore, storage and auth using this app.
this._app = (window as any)['_app'] = ObtainFirebaseApp(firebaseConfig, optionsSafe);
this._firestore = getFirestore(this._app);
this._storage = getStorage(this._app);
this._auth = getAuth(this._app);
I'm not sure about auth but in my tests the emulator config for storage and firestore will not work like this. One approach here could be to allow emulator versions of these to pass these along with the app, so in react-admin:
export const db = getFirestore(app);
if (process.env.NODE_ENV === 'development') {
connectFirestoreEmulator(db, 'localhost', 8080);
}
// etc for storage and auth, then
export const firebaseOptions: RAFirebaseOptions = {
rootRef: "",
app: db.app,
db: db,
storage: storage,
auth: auth
};
const dataProvider = FirebaseDataProvider(firebaseConfig, firebaseOptions);
const authProvider = FirebaseAuthProvider(firebaseConfig, firebaseOptions);
and in FirebaseWrapper.ts constructor:
this._firestore = this.options.db as FireStore;
this._storage = this.options.storage as FireStorage;
this._auth = this.options.auth as FireAuth;
- /src/providers/database/firebase/FirebaseWrapper.ts dbGetCollection When I debug this in the browser I can see Firebase SDK does not recognise the this._firestore object as an instanceof Firestore - for reasons I haven't had time to work out, and this despite me running react-admin and react-admin-firebase with the same SDK version (10.7.1). The specific code generatigng the "Expected first argument to collection()" error is react-admin-firebase/node_modules/@firebase/firestore/src/lite-api/reference.ts
export function collection<AppModelType, DbModelType extends DocumentData>(
parent:
| Firestore
| DocumentReference<AppModelType, DbModelType>
| CollectionReference<AppModelType, DbModelType>,
path: string,
...pathSegments: string[]
): CollectionReference<DocumentData, DocumentData> {
parent = getModularInstance(parent);
validateNonEmptyArgument('collection', 'path', path);
if (parent instanceof Firestore) {
const absolutePath = ResourcePath.fromString(path, ...pathSegments);
validateCollectionPath(absolutePath);
return new CollectionReference(parent, /* converter= */ null, absolutePath);
} else {
if (
!(parent instanceof DocumentReference) &&
!(parent instanceof CollectionReference)
) {
throw new FirestoreError(
Code.INVALID_ARGUMENT,
'Expected first argument to collection() to be a CollectionReference, ' +
'a DocumentReference or FirebaseFirestore'
);
}
Hoping this can help us get closer to a solution.