httpsCallableFromURL
is working correctly but httpsCallable
is returning null
. What's wrong?
The emulator logs show that the Cloud Function executes correctly from both httpsCallableFromURL
and httpsCallable
. My browser console logs UPPERCASE
(if I enter uppercase
) with httpsCallableFromURL
. But with httpsCallable
the browser console logs null
.
It seems like null
logs almost instantly but UPPERCASE
takes a moment to log. My guess is that the return is executing before the function?
And another question. I found the URL in the Firebase Console in the list of Functions, for the deployed function. Is there somewhere in the emulator where I can find the URL for calling the function in the emulator? This URL was written by Github Copilot! This URL also works: http://localhost:5001/my-projectId/us-central1/upperCaseMe
index.ts
import * as functions from "firebase-functions";
export const upperCaseMe = functions.https.onCall((data, context) => {
const original = data.text;
const uppercase = original.toUpperCase();
functions.logger.log('upperCaseMe', original, uppercase);
return uppercase;
});
app.component.ts
import { Component } from '@angular/core';
import { getFunctions, httpsCallable, httpsCallableFromURL } from "firebase/functions";
import { initializeApp } from 'firebase/app';
import { environment } from '../environments/environment';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
firebaseConfig = environment.firebaseConfig;
firebaseApp = initializeApp(this.firebaseConfig);
messageText: any;
functions = getFunctions(this.firebaseApp);
callMe(messageText: any) {
console.log("Calling Cloud Function: " messageText);
// const upperCaseMe = httpsCallable(this.functions, 'upperCaseMe');
const upperCaseMe = httpsCallableFromURL(this.functions, 'http://127.0.0.1:5001/my-projectId/us-central1/upperCaseMe');
upperCaseMe({ text: messageText })
.then((result) => {
console.log(result.data)
})
.catch((error) => {
console.error(error);
});;
};
}
I was able to make httpsCallableFromURL
return null
:
import * as functions from "firebase-functions";
export const upperCaseMe = functions.https.onCall((data, context) => {
const original = data.text;
const uppercase = original.toUpperCase();
setTimeout(() => {
console.log("Wait ten seconds.");
functions.logger.log('upperCaseMe', original, uppercase);
return uppercase;
}, 10000)
});
This never logs anything in the emulator and always logs null
in the browser console. It logs null
after a second or two, not ten seconds. Same results with httpsCallableFromURL
and httpsCallable
.
CodePudding user response:
httpsCallable
uses your deployed code hosted by Cloud Functions by default. The URL is derived from the data you passed during initialization of the Firebase SDK. It does not use the emulator unless you configure the SDK to do so. I suggest reviewing the documentation on the matter.
To instrument your app to interact with the emulators, you may need to do some additional configuration.
If your prototype and test activities involve callable backend functions, configure interaction with the Cloud Functions for Firebase emulator like this:
import { getApp } from "firebase/app"; import { getFunctions, connectFunctionsEmulator } from "firebase/functions"; const functions = getFunctions(getApp()); connectFunctionsEmulator(functions, "localhost", 5001);
httpsCallableFromURL
is for cases when you cannot use the normal way that the SDK locates the backend, for example, if you are hosting the callable function on some service that is not a normal Cloud Functions backend, or you are trying to invoke a function in a project that's not part of your app.
I would never expect Copilot to generate the correct code under any circumstance. It's better to lean on the documentation to tell you what to do.
CodePudding user response:
I initialized Firebase incorrectly. Firebase should be initialized in app.module.ts
, not in app.component.ts
. Firebase should then be injected into app.component.ts
. When I initialized Firebase correctly then both httpsCallable
and httpsCallableFromURL
returned the UPPERCASE message to the browser console.
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
// Angular
import { environment } from '../environments/environment';
import { FormsModule } from '@angular/forms';
// AngularFire
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
import { provideFunctions,getFunctions, connectFunctionsEmulator } from '@angular/fire/functions';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideFunctions(() => {
const functions = getFunctions();
if (!environment.production) {
connectFunctionsEmulator(functions, 'localhost', 5001);
}
return functions;
}),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
I changed the view to pass the message only via ngModel
. Passing the message via callMe(messageText)
was redundant.
app.component.html
<h3>Call cloud function</h3>
<form (ngSubmit)="callMe()">
<input type="text" [(ngModel)]="messageText" name="message" placeholder="message" required>
<button type="submit" value="Submit">Submit</button>
</form>
I changed the component controller to inject Functions
rather than initialize Firebase in the component controller:
app.component.ts
import { Component, Inject } from '@angular/core';
import { Functions, httpsCallable, httpsCallableFromURL } from '@angular/fire/functions';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(
@Inject(Functions) private readonly functions: Functions,
) {}
messageText: string = '';
callMe() {
console.log("Calling Cloud Function: " this.messageText);
const upperCaseMe = httpsCallable(this.functions, 'upperCaseMe');
// const upperCaseMe = httpsCallableFromURL(this.functions, 'http://127.0.0.1:5001/languagetwo-cd94d/us-central1/upperCaseMe');
upperCaseMe({ text: this.messageText })
.then((result) => {
console.log(result.data)
})
.catch((error) => {
console.error(error);
});;
};
}
No changes to index.ts
.