Using Firebase emulator with AngularFire
Asked Answered
A

7

34

I am trying to use the freshly introduced Firestore emulator in my Angular7 application.

According to this documentation, I run the dev server on 127.0.0.1:8080 with :

firebase serve --only firestore

Then, after ng serve, how can I make my AngularFire module use the database emulator ?

I tried the following in my environment.ts :

export const environment = {
  production: false,
  name: 'local',
  firebase: {
    databaseURL: "http://127.0.0.1:8080"
  }
};

But it does not work since it needs a "projectId". I tried to set it to my pre-production Firestore database, but then the dev server is not used.

Any thought about it ?

Here is my app.module.ts :

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from '@app/app-routing.module';
import { AppComponent } from '@app/app.component';
import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { AngularFireStorageModule } from '@angular/fire/storage';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { environment } from '@env/environment';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebase, 'my-super-cool-app'),
    AngularFirestoreModule,
    AngularFireAuthModule,
    AngularFireStorageModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
Airframe answered 19/3, 2019 at 11:21 Comment(2)
The latest firebase tools has no option --only firestore . The only valid targets are hosting and functions. As far as I now you can only serve functions and hosting locally, but not firestore. You must use the online firestore.Jacklight
@Jacklight it has, the option is in beta, see firebase.google.com/docs/firestore/security/test-rules-emulatorAirframe
H
22

I got it working by adding a provider in the main app module that overrides some of the variables set by the environment file.

See This answer to an issue at the angularfire repo. Example (from the answer):

{
  provide: FirestoreSettingsToken,
  useValue: environment.production ? undefined : {
    host: 'localhost:8081',
    ssl: false
  }
}

As i have a couple of different environments, I let the conditional look for an 'emulator' attribute in the environment and return undefined if it's false or undefined, instead of looking for the production attribute and returning undefined if it's present:

{
  provide: FirestoreSettingsToken, useValue: environment.emulator ? {
      host: 'localhost:8081',
      ssl: false
  } : undefined
}

Update as of AngularFire v6.x.x:

FirestoreSettingsToken has changed to SETTINGS in @angular/fire/firestore module

Hawserlaid answered 7/10, 2019 at 18:59 Comment(1)
Just to add, FirestoreSettingsToken can be imported from '@angular/fire/firestore' :)Loraleeloralie
C
14

Using "the Firebase emulator" is not as obvious as it first seems, as "the emulator" is actually a collection of emulators. Setting one doesn't mean setting them all. You can even set some to run locally while others are remote, depending on your needs.

To set all emulators to run locally, you can use these providers:

import { AngularFireAuthModule, USE_EMULATOR as AUTH_EMULATOR } from '@angular/fire/auth';
import { USE_EMULATOR as FIRESTORE_EMULATOR } from '@angular/fire/firestore';
import { USE_EMULATOR as DATABASE_EMULATOR } from '@angular/fire/database';
import { USE_EMULATOR as FUNCTIONS_EMULATOR } from '@angular/fire/functions';

...

  providers: [
    {
      provide: AUTH_EMULATOR,
      useValue: environment.production ? undefined : ['localhost', 9099],
    },
    {
      provide: FIRESTORE_EMULATOR,
      useValue: environment.production ? undefined : ['localhost', 8080],
    },
    {
      provide: DATABASE_EMULATOR, // i.e., Realtime Database
      useValue: environment.production ? undefined : ['localhost', 9000],
    },
    {
      provide: FUNCTIONS_EMULATOR,
      useValue: environment.production ? undefined : ['localhost', 5001],
    },
  ],
Crescentia answered 2/12, 2020 at 15:50 Comment(4)
I wish this would be found generally available in the AngularFire github repository. Thank you for this.Isbell
@Isbell github.com/angular/angularfire/blob/master/docs/emulators/…Burgas
you mention all emulators, but PUBSUB is missing, also in the github link. Does anyone have that snippet?Hellenist
I think the link ravo10 mentions is now under compat/ but otherwise identical I think: github.com/angular/angularfire/blob/master/docs/compat/…Lune
S
10

I'm able to run it directly from environment.ts file without raising any errors.

export const environment = {
    production: false,
    firebaseConfig: {
        host: 'localhost:8081',
        ssl: false,
        apiKey: '<api-key>',
        authDomain: '<project-id>.firebaseapp.com',
        databaseURL: 'http://localhost:9000?ns=<project-id>',
        projectId: '<project-id>',
        appId: '<app-id>',
        measurementId: '<measurement-id>',
    },
};
Sidoon answered 30/4, 2020 at 22:16 Comment(2)
What is <your-id> in the databaseUrlNumberless
@Numberless it depends on the field, I've updated the answer.Sidoon
R
10

based on this documentation since version 6.1.0 you could do:

import { USE_EMULATOR as USE_FIRESTORE_EMULATOR } from '@angular/fire/firestore';

@NgModule({
  // ... Existing configuration
  providers: [
    { provide: USE_FIRESTORE_EMULATOR, useValue: ['localhost', 8081] }
  ]
})
export class AppModule { } 

VERSION 7

Since version 7 you need to use these methods in your modules

...
@NgModule({
    imports: [
        provideFirebaseApp(() => {
    
          const firebaseApp = initializeApp(environment.firebaseConfig)
    
          return (firebaseApp);
    
        }),
        provideAuth(() => {
    
          const auth = getAuth();
    
          if (!environment.production)
            connectAuthEmulator(auth, `http://localhost:9099`)
    
          return (auth);
    
        }),
        provideDatabase( () => {
          
          const db = getDatabase()
        
          if ( !environment.production )
            connectDatabaseEmulator( db, 'localhost' , 9000 );
    
          return ( db );
    
        } ),
        provideFirestore( () => {
  
          const firestore = getFirestore()

          if ( !environment.production )
            connectFirestoreEmulator( firestore, 'localhost' , 8080 );

          return ( firestore );

      } ) ]
      ...
Rosina answered 1/12, 2020 at 7:6 Comment(1)
The version 7 ``provideFirestore` approach worked for me when the providers approach was failing to use the emulator.Fiducial
B
6

For one who are using AngularFire v7 without compat module, you can find an exmaple app.module.ts to use emulators. I worked with @amgular/fire v7.2.1.

Bamby answered 19/2, 2022 at 18:25 Comment(1)
Thanks this really helped meGlabrescent
A
4

Following this Article from Dev.to, I managed to setup my Angular 8 application to communicate with my emulated firebase functions, firestore and realtime db.

Firebase:

To keep this brief: The firebase configuration may be that of a registered Firebase Application (projectId, etc) set in your environment: `"firebase": {...actual firebase config here... }` or retrieved via API.

Please do not have your API keys and vital information exposed on the frontend in a production application.

How to (For those not setup): The emulating Firebase may be followed in the article above or official documentation: Setup Firebase Emulators, Run Functions Locally.

Angular:

Below are snippets from the article in regards to Angular's Configuration.

src/environments/environment.ts

{ ...
  emulator:true
}

src/app/app.module.ts

import {AngularFirestoreModule, SETTINGS} from "@angular/fire/firestore";

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
  ],
  providers: [{
    provide: SETTINGS,
    useValue: environment.emulator ? {
      host: 'localhost:8080',
      ssl: false
    } : undefined
  }],
  bootstrap: [...]
})
export class AppModule {
}

src/app/app.component.ts

Snippet from the article:

public ngOnInit() {
    if (environment.emulator) {
      this.aff.useFunctionsEmulator('http://localhost:5001').then(() => console.log('Using functions emulator'))
    }
}

Vs: What I implemented instead:

constructor(...
private app: FirebaseApp) {...}

...

ngOnInit() {
  if (environment.emulator) {
    this.app.functions().useFunctionsEmulator('http://localhost:5001');
    console.log('Using functions emulator');
  }
}

Note in article which applies to database / firestore instead of functions:

NOTE: The overall concept is the same for the other firebase client sdk's but the syntax will change between implementations. The main goal is to really just tell the firebase sdk's that you want to use a different url for that service to point to.


Angular: Why this way?

It allows you to emulate multiple environments, without changing the infrastructure of the environment (file) you're emulating (no hard-coded changes to localhost, but rather the app will change these values itself).

I would recommend only using this as a template and customizing it for your application - for instance, the localhost:8080 in app.modules can be dynamic and associated to an environment variable for tidier configuration.

Ardeha answered 18/12, 2020 at 9:7 Comment(0)
L
2

VERSION 16+ (or before too, I started on 16 here)

uses another syntax/method.

This also enables persistent cache, and sets region for functions:

// omitted some imports for brevity
import { connectFunctionsEmulator } from '@angular/fire/functions'; 
import { connectAuthEmulator } from "@angular/fire/auth";
import { connectFirestoreEmulator } from '@angular/fire/firestore';

    //provideFunctions(() => getFunctions()), // standard, from docs

    provideFunctions(() => {
      const functions = getFunctions();
      functions.region = "us-central1";
      if (!environment.production) {
        connectFunctionsEmulator(functions, "localhost", 5001);
      }
      return functions;
    }),

    provideAuth(() => {
      const auth = getAuth();
      if (!environment.production) {
        connectAuthEmulator(auth, "http://localhost:9099", {
          disableWarnings: false,
        });
      }
      return auth;
    }),

    provideFirestore(() => {
      const firestore = initializeFirestore(getApp(), {
        localCache: persistentLocalCache({
          tabManager: persistentMultipleTabManager(),
        }),
      });
      if (!environment.production) {
        connectFirestoreEmulator(firestore, "localhost", 8080);
      }
      return firestore;
    }),

and so on for the other emulators (haven't tried all of them yet)

Lune answered 18/12, 2023 at 23:44 Comment(1)
This worked for me with: "@angular/cli": "^17.1.3", "@angular/fire": "^17.0.1", "firebase": "^10.8.0", "firebase-tools": "^13.2.1", "firebaseui": "^6.1.0"Immobilize

© 2022 - 2024 — McMap. All rights reserved.