Reputation: 10930
I'm trying the 2nd generation Cloud Functions for Firebase. I have makeUppercase()
firing with onDocumentCreated()
. Now I'm trying to get an Angular app to call a callable cloud function.
index.ts
import * as logger from "firebase-functions/logger";
import { onCall } from "firebase-functions/v2/https";
import { defineSecret } from "firebase-functions/params";
const apiKey = defineSecret("12345abcde");
import { initializeApp } from "firebase-admin/app";
initializeApp();
export const CallUppercaseMe2ndGen = onCall({ secrets: [apiKey] }, (request) => {
if (request.data != undefined) {
if (request.auth != undefined) {
const text = request.data.text;
const uid = request.auth.uid;
logger.log("Uppercasing", uid, text);
const uppercase = text.toUpperCase();
return uppercase;
} else {
return null;
}
} else {
return null;
}
});
angular.component.ts
async callMe() {
const upperCaseMe = httpsCallable(this.functions, 'CallUppercaseMe2ndGen');
// const upperCaseMe = httpsCallableFromURL(this.functions, 'http://localhost:5001/my-project/us-central1/CallUppercaseMe2ndGen');
// const upperCaseMe = httpsCallableFromURL(this.functions, 'https://us-central1-my-project.cloudfunctions.net/CallUppercaseMe2ndGen');
upperCaseMe({
text: 'hello world',
})
.then((result) => {
console.log(result)
})
.catch((error) => {
console.error(error);
});
}
This throws a CORS error:
Access to fetch at 'https://us-central1/my-project.cloudfunctions.net/CallUppercaseMe2ndGen' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I know a few ways to fix a CORS error in a cloud function.
Use an IAM service account. This is my preference in 1st generation cloud functions. In the 2nd generation cloud function I put in my API key. I suspect the problem is that I need to put in more than the API key to connect a cloud function to an IAM service account. How do I do this?
Set "Access-Control-Allow-Origin" in firebase.json
. This works in a deployed cloud function but not in the emulator.
Import cors
with onRequest
. This works with onRequest
, not with onCall
. I don't use HTTP requests to call cloud functions.
Use httpsCallableFromURL
instead of httpsCallable
. Doug Stevenson has discouraged me from this solution but it sometimes fixes the CORS error.
New in the 2nd generation cloud functions, you can configure CORS in onRequest
. I'm using onCall
, not onRequest
. I tried replacing the parameter {secrets: [apiKey]}
with {cors: true}
but that didn't help.
On request I'll show code for each of these five solutions.
Upvotes: 4
Views: 4511
Reputation: 131
I had a similar problem with my emulated callable functions.
In addition to making sure you're connecting to the emulator i.e.
connectFunctionsEmulator(this.functions, "127.0.0.1", 5001);
You'll need to make sure the function you're trying to call is part of the latest build. This means the function has to be defined in your latest build: functions/lib/index.js
.
If it isn't in your latest build, you may simply run npm run build
from the /functions directory to rebuild your functions and try calling the function once again.
Upvotes: 0
Reputation: 359
This was fixed for me by going to the cloud function in google console, and if its a v2 function, it needs Cloud Run Invoker, "allUsers" or "allAuthenticatedUsers". I had thought it would be okay if it was a "Functions Invoker", but they have migrated it to Cloud Run.
Upvotes: 3
Reputation: 10930
I didn't say that the CORS errors were thrown when I was using the emulator, not the cloud. Everything is working now. I did two things:
I deployed my functions to the Firebase cloud. The first deployment ended with an error message saying that Firebase had to configure stuff for 2nd gen cloud functions. The next deployment went through and my functions worked in the cloud.
I added this code at the top of the Angular handler function:
connectFunctionsEmulator(this.functions, "127.0.0.1", 5001);
This code makes the function run in the emulator. Commenting it out makes the function run in the cloud.
The CORS errors were thrown because my Angular app was trying to call the functions in the cloud, but the functions hadn't been deployed to the cloud. I hadn't put in connectFunctionsEmulator()
so the functions weren't running in the emulator.
In other words, the CORS error were a red herring that sent me down a rabbit hole. If the error had said, "No such function found in the Firebase cloud and no emulator is connected," I would've figured out the error quickly.
I tried the same function with { secrets: [apiKey]
, with { cors: true }
, and with neither. All three worked.
Upvotes: 4
Reputation: 21685
Had the same problem. I'm not sure which one of these actions (or all of them) solved the problem, but here's what I did.
import { setGlobalOptions } from 'firebase-functions/v2'
// locate all functions closest to users
setGlobalOptions({ region: "us-central1" })
Create a newly named function that's just a copy of your previous function, but includes {cors: true}
export const myFunctionCopy = onCall({ cors: true }, async (request) =>
{
...
}
Point your front-end code to this newly named function accordingly.
Deploy
I suspect there's some sort of cache / propagation issue going on with Google's backend. Renaming the function (as opposed to just updating the code) avoids this.
Upvotes: 5