Loheek
Loheek

Reputation: 1985

Using Firebase emulator with AngularFire

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 { }

Upvotes: 35

Views: 12132

Answers (7)

Arno Teigseth
Arno Teigseth

Reputation: 349

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)

Upvotes: 2

JSmith
JSmith

Reputation: 4808

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

      } ) ]
      ...

Upvotes: 10

taro
taro

Reputation: 719

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.

Upvotes: 6

uɥƃnɐʌuop
uɥƃnɐʌuop

Reputation: 15113

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],
    },
  ],

Upvotes: 14

KeaganFouche
KeaganFouche

Reputation: 681

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.

Upvotes: 4

runelk
runelk

Reputation: 324

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

Upvotes: 22

Rami Alloush
Rami Alloush

Reputation: 2626

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>',
    },
};

Upvotes: 10

Related Questions