Search code examples
javascriptfirebasegoogle-cloud-firestoremonorepo

Passing Firestore (v9) instance across packages


I'm trying to write some Firestore operations into a separate package so that it could be imported and reused in different web apps. I'm building a monorepo with different packages and I'm trying to use Firebase v9 for the following example:

From packageA I'm defining and exporting a getPosts(db) function that takes in a Firestore object and returns some posts form the given database

// in 'packageA'
import { collection, getDocs, Firestore } from 'firebase/firestore';

export const getPosts = async (db: Firestore) => {
  console.log('Passed in db: ', db); // This correctly prints the passed in Firestore object

  try {
    const postsCollection = collection(db, 'posts'); // This function will throw
    const querySnapshot = await getDocs(postsCollection);

    return querySnapshot.docs.map((doc) => doc.data());
  } catch (e) {
    console.error('Error reading posts: ', e);
  }
}

In a web app I'm initialising the Firebase app and exporting the Firestore instance

// firebase.js in 'web-app-1'
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';

const firebaseConfig = { /* my Firebase config */ };

export const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);

Then I'm trying to use the getPosts function from the package in a component...

// App.js in 'web-app-1'
import { db } from './firebase.js';
import { getPosts } from 'packageA';

let posts;

async function loadPosts() {
  try {
    posts = await getPosts(db);
  } catch (e) {
    console.error(e);
  }
}

loadPosts(); // throws an error

but I get the following error from the collection(db, 'posts') call

Error reading posts: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore

even though the passed in database is correctly printed in the console (form the getPosts function)

Note: If I copy the whole getPosts function and use it directly in the web app (i.e. without importing it from another package) then it works and correctly fetches the posts.


Solution

  • I've been looking around a bit more and found this answer to a similar question to solve my problem too.

    Basically what I had to do is to specify Firebase as a peerDependency in packageA and not include it in the final bundle. The web apps that consume packageA will include Firebase as a regular dependency.

    So the package.json files look as follows

    In the utility package

    {
      "name": "packageA",
      "peerDependencies": {
        "firebase": "^9.6.3"
      }
    }
    

    and then in the web apps

    {
      "name": "web-app-1",
      "dependencies": {
        "firebase": "^9.6.3",
      }
    }
    

    This approach also makes sense to my use case as the web app – and only the web app – that initialises the Firebase app will include it in its bundle. I can imagine however that in some other use cases this is not a possible solution.

    Nevertheless I have submitted my issue to the Firebase support as suggested and here is their answer:

    We have received some similar cases and we are already working to solve this. However, it can take a while due the workload of the engineering team, please, be patient.