Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call Firebase Cloud Functions from Angular and AngularFire?

I'm trying to understand the AngularFireFunctions documentation. I made a new Angular project and a new Firestore database, installed AngularFire and Firebase, hooked up the Firebase credentials to environments.ts, and initialized firebase-functions, firebase-admin, and firestore.

I fixed a bug in functions/package.json. The initialization program creates this line:

"main": "lib/index.js",

which should be

"main": "src/index.ts",

My directory structure looks like this:

myproject
 +- .firebaserc    # Hidden file that helps you quickly switch between
 |                 # projects with `firebase use`
 |
 +- firebase.json  # Describes properties for your project
 |
 +- functions/     # Directory containing all your functions code
      |
      +- node_modules/ # directory where your dependencies (declared in # package.json) are installed
      |
      +- package-lock.json
      |
      +- src/
          |
           +- index.js  # main source file for your Cloud Functions code
      |
      +- tsconfig.json  # if you chose TypeScript
      |
      +- package.json  # npm package file describing your Cloud Functions code

I spun up a new Angular project and set up app.module.ts exactly as the AngularFireFunctions documentation recommends:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireFunctionsModule, USE_EMULATOR } from '@angular/fire/compat/functions';
import { environment } from '../environments/environment';

@NgModule({
  imports: [
    BrowserModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireFunctionsModule
  ],
  declarations: [ AppComponent ],
  bootstrap: [ AppComponent ],
  providers: [
    { provide: USE_EMULATOR, useValue: ['localhost', 5001] }
   ]
})
export class AppModule {}

I made a button in my HTML view to call the Firebase Cloud Function:

<div>
    <button mat-raised-button color="basic" (click)='callMe()'>Call me!</button>
</div>

I imported firebase-functions and firebase-admin into index.ts as the documentation recommends. Then I uncommented the default function that comes with index.ts and added a console.log.

// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();

export const helloWorld = functions.https.onRequest((request, response) => {
  console.log("Hello world!")
  functions.logger.info("Hello logs!", {structuredData: true});
  response.send("Hello from Firebase!");
});

Finally we get to app.component.ts. I don't understand the provided code in the documentation and it throws errors so I wrote my own controller:

import { Component } from '@angular/core';
import { AngularFireFunctions } from '@angular/fire/compat/functions';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private fns: AngularFireFunctions) {}
 
  callMe() {
    console.log("Calling...");
    this.fns.httpsCallable('helloWorld');
  }
}

When I run firebase emulators:start I see an error message:

functions: Failed to load function definition from source: FirebaseError: Failed to load function definition from source: Failed to generate manifest from function source: SyntaxError: Unexpected token 'export'

It's objecting to these lines in app.module.ts and app.component.ts:

export class AppModule {}

export class AppComponent {}

Those aren't errors and the emulator recovers and starts up.

 ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000   

I click the button in my HTML view, see Calling... in the console log, and nothing happens in the emulator log. I was expecting to see Hello world in the emulator logs. Why doesn't my Angular app call the Firebase Cloud Function?

like image 347
Thomas David Kehoe Avatar asked Aug 23 '26 06:08

Thomas David Kehoe


1 Answers

I've written a tutorial that answers this question. Here's the app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { environment } from '../environments/environment';

// AngularFire 7
// import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
// import { provideFirestore, getFirestore } from '@angular/fire/firestore';
// import { provideFunctions, getFunctions, connectFunctionsEmulator } from '@angular/fire/functions'; // https://firebase.google.com/docs/emulator-suite/connect_functions#instrument_your_app_to_talk_to_the_emulators

// AngularFire 6
import { AngularFireModule } from '@angular/fire/compat';
import { AngularFireFunctionsModule } from '@angular/fire/compat/functions';
import { USE_EMULATOR } from '@angular/fire/compat/functions'; // comment out to run in the cloud

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,

    // AngularFire 7
    // provideFirebaseApp(() => initializeApp(environment.firebase)),
    // provideFirestore(() => getFirestore()),
    // provideFunctions(() => getFunctions()),

    // AngularFire 6
    AngularFireModule.initializeApp(environment.firebase),
    AngularFireFunctionsModule
  ],
  providers: [
    { provide: USE_EMULATOR, useValue: ['localhost', 5001] } // comment out to run in the cloud
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

This uses AngularFire 6. I haven't been able to get AngularFire 7 to work with callable functions.

This runs the functions in the emulator. To run your functions in the cloud comment out two lines.

Here's app.component.ts:

import { Component } from '@angular/core';

// AngularFire 7
// import { getApp } from '@angular/fire/app';
// import { provideFunctions, getFunctions, connectFunctionsEmulator, httpsCallable } from '@angular/fire/functions'; // https://firebase.google.com/docs/emulator-suite/connect_functions#instrument_your_app_to_talk_to_the_emulators
// import { Firestore, doc, getDoc, getDocs, collection, updateDoc } from '@angular/fire/firestore';

// AngularFire 6
import { AngularFireFunctions } from '@angular/fire/compat/functions';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent {
  data$: any;

  constructor(private functions: AngularFireFunctions) {
    const callable = this.functions.httpsCallable('executeOnPageLoad');
    this.data$ = callable({ name: 'Charles Babbage' });
  }

  callMe() {
    console.log("Calling...");
    const callable = this.functions.httpsCallable('callMe');
    this.data$ = callable({ name: 'Ada Lovelace' });
  };
}

Again this AngularFire 6. The variable data$ handles the data returned from the cloud function.

httpsCallable takes one parameter, the name of the function.

callable executes the function and takes one parameter, an object holding the data to send to the function.

The HTML view:

<div>
    <button mat-raised-button color="basic" (click)='callMe()'>Call me!</button>
</div>

{{ data$ | async }}

The view show a button for the user the click and the data returned from the function.

And the index.js cloud functions:

// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();

// executes on page load
exports.executeOnPageLoad = functions.https.onCall((data, context) => {
    console.log("The page is loaded!")
    console.log(data);
    console.log(data.name);
    // console.log(context);
    return 22
});

// executes on user input
exports.callMe = functions.https.onCall((data, context) => {
    console.log("Thanks for calling!")
    console.log(data);
    console.log(data.name);
    // console.log(context);
    return 57
});

Each functions uses https.onCall((data, context) => {} to make it a callable function, i.e., callable from Angular. data is the data sent from Angular. context is metadata about the execution of the function. Each function returns data, which is displayed in the HTML view.

To run the functions use the emulator:

firebase emulators:start --only functions
like image 114
Thomas David Kehoe Avatar answered Aug 26 '26 16:08

Thomas David Kehoe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!