Optimizing Firebase with Region-Specific Firestore Instances

Ariyan AshfaqueAriyan Ashfaque
3 min read

Introduction

When building applications that need to access multiple Firestore databases based on a user’s location or other criteria, it’s essential to dynamically initialize and use the correct Firestore instance after user authentication. This blog will guide you through setting up multiple Firestore instances in an Ionic and Angular app, fetching the appropriate instance based on the user’s region, and using it within your application.

Configuring Firebase Authentication

To start, ensure Firebase Authentication is properly configured in your Ionic and Angular app. Initially, you'll only configure the default Firebase project, which will handle user authentication.

import { NgModule } from '@angular/core';
import { IonicModule } from '@ionic/angular';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { BrowserModule } from '@angular/platform-browser';
import { getAuth, provideAuth } from '@angular/fire/auth';
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
import { getFirestore, provideFirestore } from '@angular/fire/firestore';

const firebaseConfigDb1 = { /* Config for db1 (default) */ };

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,
    IonicModule.forRoot(),
    provideAuth(() => getAuth()),
    provideFirestore(() => getFirestore()),
    provideFirebaseApp(() => initializeApp(firebaseConfigDb1)),
  ],
  bootstrap: [AppComponent],
})

export class AppModule {}

Initializing Multiple Firestore Instances

After authentication, the next step is to initialize the appropriate Firestore instance based on the user's region. This is done by creating service accounts for each Firebase project, configuring them, and initializing them dynamically.

import { RegionService } from './region.service';
import { Auth, onAuthStateChanged } from '@angular/fire/auth';
import { initializeApp, FirebaseApp } from '@angular/fire/app';
import { getFirestore, Firestore } from '@angular/fire/firestore';

const firebaseConfigDb2 = { /* Config for db2 (US) */ };
const firebaseConfigDb3 = { /* Config for db3 (UK) */ };

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private db2: Firestore | null = null;
  private db3: Firestore | null = null;

  constructor(private auth: Auth, private regionService: RegionService) {
    this.initAuthStateListener();
  }

  initAuthStateListener() {
    onAuthStateChanged(this.auth, (user) => {
      if (user) {
        this.regionService.getUserRegion().then((region) => {
          if (region === 'us') {
            this.db2 = getFirestore(initializeApp(firebaseConfigDb2));
          } else if (region === 'uk') {
            this.db3 = getFirestore(initializeApp(firebaseConfigDb3));
          }
        });
      }
    });
  }

  getFirestoreInstance(): Firestore | null {
    const region = localStorage.getItem('userRegion');
    if (region === 'us' && this.db2) {
      return this.db2;
    } else if (region === 'uk' && this.db3) {
      return this.db3;
    }
    return null;
  }
}

Creating a Service for Authentication and Database Initialization

In the above example, the AuthService dynamically initializes the appropriate Firestore instance after detecting a user's login status. The service listens for authentication state changes and fetches the user's region to initialize the relevant Firestore instance.

Fetching User's Region

To determine which Firestore instance to initialize, you need to fetch the user's region. This can be done by storing the region in a Firestore collection or as part of the user profile.

import { Auth } from '@angular/fire/auth';
import { Injectable } from '@angular/core';
import { doc, getDoc, Firestore } from '@angular/fire/firestore';

@Injectable({
  providedIn: 'root'
})
export class RegionService {
  constructor(private auth: Auth, private firestore: Firestore) {}

  getUserRegion(): Promise<string> {
    const user = this.auth.currentUser;
    if (user) {
      const userDocRef = doc(this.firestore, `users/${user.uid}`);
      return getDoc(userDocRef).then((doc) => {
        if (doc.exists()) {
          return doc.data()?.region || 'us';
        } else {
          return 'us';
        }
      });
    }
    return Promise.resolve('us');
  }
}

Using Firestore in Your Components

After initializing the correct Firestore instance, you can use it in your components to query or update data.

import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
import { Component, OnInit } from '@angular/core';
import { collection, collectionData } from '@angular/fire/firestore';

@Component({
  selector: 'app-home',
  templateUrl: './home.page.html',
  styleUrls: ['./home.page.scss'],
})
export class HomePage implements OnInit {
  users$: Observable<any[]> | null = null;

  constructor(private authService: AuthService) {}

  ngOnInit() {
    const firestore = this.authService.getFirestoreInstance();
    if (firestore) {
      const usersCollection = collection(firestore, 'users');
      this.users$ = collectionData(usersCollection);
    }
  }
}

Conclusion

By following this guide, you can dynamically initialize and access multiple Firestore instances in your Ionic and Angular application based on the user's region. This approach ensures that your app is scalable and can handle region-specific data efficiently.

0
Subscribe to my newsletter

Read articles from Ariyan Ashfaque directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ariyan Ashfaque
Ariyan Ashfaque