@jvccode/firestore-factory v18.0.4
Firestore-Factory
Firestore-Factory is a library that allows you to connect to multiple Firebase projects and multiple Firestore instances (databases). This library was built with version 18.0.1 of Angular CLI and version 18.0.1 of @angular/fire. This library is in development, so not all features may work correctly yet.
How does it works
1.- Run npm i @jvccode/firestore-factory
in your project to get the library.
2.- This library requires a configuration object that you can add into environment.ts file.
the "firebaseFactoryProviderConfig" object is an array of firebase project configurations as follows:
export const environment = { production: false, firebaseFactoryProviderConfig: [ // the below config is the basic configuration // the first config will be the default configuration { // projectName and firebaseConfig are required and is the data you get from the firebase console projectName: 'projectName1', firebaseConfig: { apiKey: 'xxxxxxxxxxx', authDomain: 'xxxxxxxxxxx', databaseURL: 'xxxxxxxxxxx', projectId: 'xxxxxxxxxxx', storageBucket: 'xxxxxxxxxxx', messagingSenderId: 'xxxxxxxxxxx', appId: 'xxxxxxxxxxx', measurementId: 'xxxxxxxxxxx' } }, // the second config contains all of the posible values you can set { // projectName and firebaseConfig are required and is the data you get from the firebase console projectName: 'projectName2', firebaseConfig: { apiKey: 'xxxxxxxxxxx', authDomain: 'xxxxxxxxxxx', databaseURL: 'xxxxxxxxxxx', projectId: 'xxxxxxxxxxx', storageBucket: 'xxxxxxxxxxx', messagingSenderId: 'xxxxxxxxxxx', appId: 'xxxxxxxxxxx', measurementId: 'xxxxxxxxxxx' }, // if you include authentication then firebase authentication will be provided as a service authentication: { enabled: true, // emulatorConfig is optional emulatorConfig: { url: 'url' } }, // if you include realTimeDatabase then firebase realTimeDatabase will be provided as one instance (default) realTimeDatabase: { enabled: true, // emulatorConfig is optional emulatorConfig: { host: 'localhost', port: "8090", // options is optional and could the values be: mockUserToken?: string | EmulatorMockTokenOptions | undefined options: {mockUserToken:"xxxxxxx"} } }, // if you include storage then firebase storage will be provided as a service storage: { enabled: true, // emulatorConfig is optional emulatorConfig: { host: 'localhost', port: "8091", // options is optional and could the values be: mockUserToken?: string | EmulatorMockTokenOptions | undefined options: {mockUserToken:"xxxxxxx"} } }, // if you include firestoreDatabase then firebase firestoreDatabase will be provided as a service // you need to specify the instances (firestore databases) you want to connect // you need to provide the databaseId for each one, and for the default use 'default' as the id. // you can also provide a name so that will be easy to identify your database your wannt to interact with. // if you don't provide a name, the databaseId will be used as a name // ** if you don't include firestoreDatabase, the framework will create the default instance firestoreDatabase: { instances: { // by default firebase firestore creates the first instance with database id = 'default' databaseId: 'default', databaseName: 'defaultName', // this is optional and if provided as true, the will enable the Multi Tab Persistency enableMultiTabPersistency: true, // emulatorConfig is optional emulatorConfig: { host: 'localhost', port: "8092", // options is optional and could the values be: mockUserToken?: string | EmulatorMockTokenOptions | undefined options: {mockUserToken:"xxxxxxx"} } }, { databaseId: 'SecondDatabase', databaseName: 'lookups', enableMultiTabPersistency: true, }, { databaseId: 'thirdDatabase', databaseName: 'settings' } }, // if you include any of the below configurations then firebase RemoteConfig, Analitycs, Messaging, etc will be provided as one instance enableRemoteConfig: true, enableAnalitycs: true, enableMessaging: true, enablePerformance: true, enableFunctions: true, enableAppCheck: true, enableVertexAI: true } ] };
3.- if you are using modules in your angular application, then add the following in your app.module.ts file:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FirestoreFactoryModule } from '@jvccode/firestore-factory';
import { environment } from 'src/environments/environment';
import { AppComponent } from './app.component';
@NgModule({ declarations: AppComponent , imports: BrowserModule, FirestoreFactoryModule.initializeFactoryService(environment.firebaseFactoryProviderConfig) , providers: [], bootstrap: AppComponent }) export class AppModule { }
4.- if you are using standalone version then do the following:
in app.config.ts file, add the initializer in the providers section as follows:
export const appConfig: ApplicationConfig = { providers: provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), importProvidersFrom(FirestoreFactoryModule.initializeFactoryService(environment.firebaseFactoryProviderConfig)), provideAnimations() };
How to use it
FirestoreFactoryModule exposes a service called FirebaseFactoryService, so you can inject it into the constructor of your component/class:
constructor(private firebaseFactory: FirebaseFactoryService){}
The FirebaseFactoryService exposes the method getServiceProvider(projectName?: string), this method will return the firebaseProvider for the corresponding firebase project. If you don't provide any project name, by default the FirebaseFactoryService will return the first instance of the firebaseFactoryProviderConfig.
const myFirstFirebaseProjectInstance = this.firebaseFactory.getServiceProvider('projectName1'); OR const myFirstFirebaseProjectInstance = this.firebaseFactory.getServiceProvider(); both will return the first firebase instance as "projectName1" is the first configuration:
firebaseFactoryProviderConfig: [
// the below config is the basic configuration
// the first config will be the default configuration
{
// projectName and firebaseConfig are required and is the data you get from the firebase console
projectName: 'projectName1',
firebaseConfig: {
apiKey: 'xxxxxxxxxxx',
authDomain: 'xxxxxxxxxxx',
databaseURL: 'xxxxxxxxxxx',
projectId: 'xxxxxxxxxxx',
storageBucket: 'xxxxxxxxxxx',
messagingSenderId: 'xxxxxxxxxxx',
appId: 'xxxxxxxxxxx',
measurementId: 'xxxxxxxxxxx'
}
},
// the second config contains all of the posible values you can set
{
// projectName and firebaseConfig are required and is the data you get from the firebase console
projectName: 'projectName2',
firebaseConfig: {
apiKey: 'xxxxxxxxxxx',
authDomain: 'xxxxxxxxxxx',
databaseURL: 'xxxxxxxxxxx',
projectId: 'xxxxxxxxxxx',
storageBucket: 'xxxxxxxxxxx',
messagingSenderId: 'xxxxxxxxxxx',
appId: 'xxxxxxxxxxx',
measurementId: 'xxxxxxxxxxx'
},
...
const myProject2 = this.firebaseFactory.getServiceProvider('projectName2'); // if you want to get another instance which is not the default one, the you will need to provide the name that matches the name in the firebaseFactoryProviderConfig
// the second config contains all of the posible values you can set
{
// projectName and firebaseConfig are required and is the data you get from the firebase console
projectName: 'projectName2',
firebaseConfig: {
apiKey: 'xxxxxxxxxxx',
authDomain: 'xxxxxxxxxxx',
databaseURL: 'xxxxxxxxxxx',
projectId: 'xxxxxxxxxxx',
storageBucket: 'xxxxxxxxxxx',
messagingSenderId: 'xxxxxxxxxxx',
appId: 'xxxxxxxxxxx',
measurementId: 'xxxxxxxxxxx'
},
...
Once you get the specific firebase service provider, then you can get the firestore collections providers.
const myFirstFirebaseProjectInstance = this.firebaseFactory.getServiceProvider();
if you have only 1 database in your configuration, you can get the provider as: // fromFirestore expects the (dataBaseName?: string) param, so if you do not provide any database name, then the service will return the default instance const myUsersCollection = myFirstFirebaseProjectInstance.fromFirestore().getCollectionProvider('users'); OR const myUsersCollection = myFirstFirebaseProjectInstance.fromFirestore().getCollectionProvider('users');
if you have multiple databases, then you need to provide the name of the database you want to get the collection reference: const myUsersCollection = myFirstFirebaseProjectInstance.fromFirestore('staging').getCollectionProvider('users');
With the collectionProvider you can query the data, below are some examples:
this.myUsersCollection
.getCollectionData()
.subscribe( users => {
console.log('whole users collection');
console.log(users);
});
this.myUsersCollection
.where('rol','==','Admin')
.getCollectionSnapShot()
.subscribe( users => {
console.log('Users where rol == admin');
// this response includes the complete firestore snapshot data like "changeType", "id", etc
});
this.myUsersCollection
.getCollectionSnapShot({ orderBy: {propertyName:'firstName', sortOrder:"desc"}})
.subscribe( users => {
console.log('Snapshot -> users with filters=null (no where statement), includedId= undefined, orderby firstName, desc');
console.log(users);
});
this.myUsersCollection
.where('rol','==','Agent')
.getCollectionData({ includeId: true, orderBy: {propertyName:'firstName', sortOrder:"asc"}})
.subscribe( users => {
console.log('users with filters= role: Agente, includedId= true, orderby firstName, asc');
console.log(users);
});
this.myUsersCollection
.where('rol','==','Operator')
.getFirstOrDefault({ includeId: true, orderBy: {propertyName:'firstName', sortOrder:"asc"}})
.subscribe( users => {
console.log('users first or default');
console.log(users);
});
this.myUsersCollection
.where('isActive', '==',true)
.getCollectionDataGroupedBy({
includeId:true,
groupBy:{
properties:['rol', 'emailVerified'],
orderBy: {
propertyName:'firstName',
sortOrder:"asc"
},
sortGroupOrder:'asc'
}
})
.subscribe( users => {
console.log('users grouped by [rol, emailVerified]');
console.log(users);
});