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