react-admin-firebase icon indicating copy to clipboard operation
react-admin-firebase copied to clipboard

QUESTION: How to use with the emulator

Open nicoladj77 opened this issue 3 years ago • 12 comments

Hi, is this possible to use this data provider with the Firebase Emulator for firestore and firebase?

nicoladj77 avatar Apr 16 '22 22:04 nicoladj77

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.

benwinding avatar Apr 19 '22 01:04 benwinding

I get an error when calling it like that:

Uncaught TypeError: this.app.firestore is not a function

rzmz avatar May 17 '22 12:05 rzmz

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 avatar May 25 '22 22:05 MarcoDaniele

@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;

SKempin avatar Sep 25 '23 21:09 SKempin

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 avatar Sep 25 '23 21:09 MarcoDaniele

@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

SKempin avatar Sep 26 '23 08:09 SKempin

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 avatar Sep 26 '23 11:09 MarcoDaniele

@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.

SKempin avatar Sep 26 '23 12:09 SKempin

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.

julienV avatar Sep 28 '23 22:09 julienV

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.

SKempin avatar Sep 29 '23 06:09 SKempin

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'm getting the same error. Supporting emulators is very important, especially while prototyping or developing using the free tier of Firebase...

marcello-palmitessa avatar Oct 06 '23 19:10 marcello-palmitessa

Emulator support would be great. I had a quick look at the code and I came up against two issues;

  1. ./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;
  1. /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.

stuart-thorn avatar Jan 07 '24 19:01 stuart-thorn