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 { }
Loheek
  • 1,865
  • 17
  • 28
  • 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`. – fitzmode Mar 19 '19 at 14:35
  • @fitzmode it has, the option is in beta, see https://firebase.google.com/docs/firestore/security/test-rules-emulator – Loheek Mar 19 '19 at 14:44

6 Answers6

23

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

runelk
  • 334
  • 3
  • 10
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],
    },
  ],
uɥƃnɐʌuop
  • 14,022
  • 5
  • 58
  • 61
  • 3
    I wish this would be found generally available in the AngularFire github repository. Thank you for this. – Jessy Jan 17 '21 at 01:49
  • 3
    @Jessy https://github.com/angular/angularfire/blob/master/docs/emulators/emulators.md – ravo10 May 28 '21 at 15:52
  • you mention all emulators, but PUBSUB is missing, also in the github link. Does anyone have that snippet? – Vincent Gerris Jul 07 '22 at 23:35
  • I think the link ravo10 mentions is now under compat/ but otherwise identical I think: https://github.com/angular/angularfire/blob/master/docs/compat/emulators/emulators.md – Arno Teigseth Jun 26 '23 at 15:03
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>',
    },
};
Rami Alloush
  • 2,308
  • 2
  • 27
  • 33
9

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 );

      } ) ]
      ...
JSmith
  • 4,519
  • 4
  • 29
  • 45
  • The version 7 ``provideFirestore` approach worked for me when the providers approach was failing to use the emulator. – Mike Poole Jul 20 '22 at 16:29
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.

taro
  • 699
  • 1
  • 9
  • 20
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.

KeaganFouche
  • 581
  • 5
  • 12