Class-level modularization incompatible with fully treeshakeable JS SDK #1238
Replies: 2 comments 2 replies
-
I've answered a similar question at firebase/firebase-js-sdk#4651 TLDR is that the API similarity between JS and Admin SDKs is not a goal for us. The past experiences with users have shown us that this syntactic similarity can actually cause issues that are hard to identify and debug (due to the subtle differences between the 2 API surfaces). Also the similarity you see today is only limited to the Firestore and RTDB products, and is not generally true about Firebase SDKs. What this means is that going forward, developers that implement Firestore/RTDB compatibility layers between JS and Admin will have to use both syntax styles: if (nodejs) {
const { getFirestore } = require('firebase-admin/firestore');
const snap = await getFirestore().collection('profiles).doc(id).get();
return snap.data();
} else {
const { getFirestore, doc, getDoc } = require('firebase/firestore');
const snap = await getDoc(doc(getFirestore(), 'profiles', id));
return snap.data();
} Or a cleaner implementation could be: abstract class Profile {
public static getById(id: string): Promise<Profile> {
if (nodejs) {
return AdminProfile.getById(id);
}
return WebProfile.getById(id);
}
abstract foo: string;
abstract bar: string;
}
class WebProfile extends Profile {
public static getById(id: string): Promise<WebProfile> {
// Use web SDK
}
}
class AdminProfile extends Profile {
public static getById(id: string): Promise<AdminProfile> {
// Use Admin SDK
}
} From a more operational point of view, the server-side Firestore API is actually part of a whole other library: https://github.com/googleapis/nodejs-firestore. It might be worth reporting this feedback in that repo, and see if there's more interest in use cases like this. |
Beta Was this translation helpful? Give feedback.
-
@hiranya911 thanks for your answer. I ended up writing a compatibility module for my needs (I'm only using firestore When using an admin Firestore datatbase I'm remaping {
"baseUrl": "src",
"paths": {
"@firebase/firestore": ["admin-stuff/adminCompat"]
}
} So the imports are made from this file: // adminCompat.ts
// `lib` path added to make it work in Jest 27
import { Firestore } from 'firebase-admin/lib/firestore'
import type {
CollectionReference, DocumentReference, UpdateData,
} from 'firebase-admin/firestore'
export const collection = (firestore: Firestore, ...pathSegments: string[]) =>
firestore.collection(pathSegments.join('/'))
export const doc = (dbOrCol: Firestore|CollectionReference, ...pathSegments: string[]) => {
const [colPath, ...docPathSegments] = pathSegments
if (dbOrCol instanceof Firestore) {
// doc(db, colPath, docPathSegments)
return dbOrCol.collection(colPath).doc(docPathSegments.join('/'))
} else {
// doc(collection, pathSegments) or doc(collection)
return colPath ? dbOrCol.doc(pathSegments.join('/')) : dbOrCol.doc()
}
}
export const getDoc = <T>(reference: DocumentReference<T>) =>
reference.get()
export const writeBatch = (firestore: Firestore) =>
firestore.batch()
// TODO dataOrField
export const updateDoc = (reference: DocumentReference, data: UpdateData) =>
reference.update(data)
export const deleteDoc = (reference: DocumentReference) =>
reference.delete()
// admin types
export type {
Timestamp, FirestoreDataConverter, QueryDocumentSnapshot, DocumentData
} from 'firebase-admin/firestore' Now I can use the same code with an admin and a non admin Firestore database. Functions are translated from the modular to the chained syntax. As you said there are some subtle differences between the two API surfaces. It worked for me with some little tweaks but it is hard to make TypeScript happy sometimes 😉 Hope it helps some folks. |
Beta Was this translation helpful? Give feedback.
-
Hi, kudos for your efforts on making Firebase treeshakeable.
With the old "chained" syntax that was used by both firebase-admin and the js sdk, I was able to use the same CRUD primitives library in my clients and on my local server for administrative tasks (e.g.
Profiles.create(…)
,Profile.getById(…)
etc.). I was doing so simply by using an admin/non-admin firestore database object in my library.But with the new different syntaxes of firebase v9+ and firebase-admin v9+, I have to write separate codebases for my clients and my server even if I’m doing the exact same things on both sides.
If I pass an admin-firestore object to my non-admin firebase primitives, of course I get an error:
I read the reasons why you stopped at the class level, but is preserving most of the old style really worth bifurcating from the JS SDK and force developers to remember two syntaxes (if not duplicating their codebases)? 😕
Beta Was this translation helpful? Give feedback.
All reactions