How to access environment-specific Firebase Function endpoints from Firebase Hosted application? - firebase

I have three Firebase projects representing Development, Staging and Production environments hosted on Firebase hosting. Each environment utilizes its own deployed Firebase functions like so:
Dev function endpoint: https://us-central1-my-app-dev.cloudfunctions.net/someFunction
Staging function endpoint: https://us-central1-my-app-staging.cloudfunctions.net/someFunction
Production function endpoint: https://us-central1-my-app.cloudfunctions.net/someFunction
I can't figure out how the static, Firebase-hosted client React application should invoke these functions because the URI endpoints of each changes depending on which environment the code is executing from.
Ideally I could set environment-specific configuration for each Firebase Hosting environment; unfortunately the only way to do this in Firebase Hosting is from within Firebase Functions themselves.
How can I retrieve the environment-specific endpoint for each Firebase Function?

You have a couple options here.
First, you could just configure your React app any way you like. It's necessarily not a bad thing for each system component (backend, frontend) to have its own configuration.
Second, since you're using Firebase Hosting to serve your static content, you can also use it to serve your functions API endpoints. This means that both your static content and API endpoints are all served through the same hostname, which means you no longer have to specify the host when making a request. All the requests can be relative to that host. You can achieve this via Hosting rewrite rules.

Related

How to use Cloud run from different project/domain?

I have Cloud run function deployed on one different project than a webapp hosted on firebase.
In firebase documentation it provides a way to call Cloud Run containers, but I can only specify serviceId and that service is not running in the same project as firebase container, so if I try to deploy it any way I get error 400.
Error: HTTP Error: 400, Cloud Run service `service-name` does not exist in region `europe-north1` in this project.
I did not see a way to specify a project id in the rewrites config.
Is there a way to call for firebase projects to call Cloud run containers from a different project?
P.S. I am aware that I can call it over "regular" HTTPS however I would like to reduce the traffic and maybe even some billing.

Firebase Hosting is not making calls to External APIS, which config should I add to Firebase.json to make this work?

I'm building web app with flutter web and I'm using firebase hosting to serve the web app.
The web app makes an API post request to the static IP of my aws ec2 instance to get a response
const String api = 'http://7.91.300.2411:8080/predict';
Map<String, String> upload = {'data1': _data1, 'data2': _data2};
var _body = jsonEncode(upload);
final Uri uri = Uri.parse(api);
http.Response response =
await http.post(uri, headers: _headers, body: _body);
When I run the app on development, like on my local machine, it works, I see the logs on my ec2 server saying the endpoint was hit/called, and then it runs successfully.
But when I now deployed this web app to firebase hosting, the endpoint never gets hit/called, is as if the http post request was not made.
I've upgraded my firebase plan to blaze, yet it's still not working. I also enable cors on my ec2 instance.
Something I taught could solve the problem, was to edit the firebase.json file, and add some config to enable firebase hosting allow calls to external apis, but I don't know how to go about it
Actually, Firebase Hosting does not make calls to external APIs. Firebase Hosting "serves both static and dynamic content to a global CDN (content delivery network)". In the case of a Flutter web app, Firebase Hosting serves the files that were generated in the /build/web directory of your project when you built the app for deployment (aka the app release bundle).
The only scenario I can see that could be considered as Firebase Hosting making calls to external APIs is if you pair Firebase Hosting with Cloud Functions or Cloud Run, which, when they are executed, call the external API. Strictly speaking, in this scenario, Firebase Hosting does not call an API but serves dynamic content that was generated via a call to an API.
If I'm not mistaking, by looking at your code, we can conclude that this scenario does not apply to your case. In your case, Firebase Hosting just hosts your Flutter app release bundle.
In your case, you are using the Dart http package in order to call the API from your web page/app. I don't see any reason why Firebase Hosting would prevent that: Your browser has downloaded the web page from Firebase Hosting and then the web page initiates, from your browser, a direct call to an API (without calling Firebase Hosting). So most probably the problem comes from something else.

How to get the firebase functions URL in a firebase hosted webapp?

I am working on a project in which we have pre-existing cloud functions in use with Firebase. We are adding a small React SPA using firebase cloud hosting, and this SPA will interact with some of the existing public cloud functions.
The way we have been doing things so far, we have a dev project, and a production project in Firebase. For cloud functions, this works fine, we have environment specific config set up with firebase functions:config:set for differentiations between prod and dev servers.
The problem comes with the hosted SPA contacting the cloud functions. I've seen a lot of questions on how to access the environment config in the hosted code, eg this one: How to reference Firebase Functions config variables from a Firebase-hosted application? where the answer seems to be to have firebase functions that return the values of the environment variables, but for me this just moves the problem further back on step. I fully understand that having the environment variables accessible to this code would be a massive security problem as the SPA is run in the browser.
The only environment specific config I really need for the hosted SPA is the base address for the cloud functions.
eg if in my cloud functions I have
const functions = require('firebase-functions');
const express = require('express');
const test = express();
test.on('/hello/:target', (req, res) => {
res.send(`Hello ${req.params.target}`);
})
exports.test = functions.https.onRequest(test);
then having deployed, this cloud function is available both at https://us-central1-DEV-PROJECT-NAME.cloudfunctions.net/test/hello/world and https://us-central1-PROD-PROJECT-NAME.cloudfunctions.net/test/hello/world . How would I best get the appropriate root url (https://us-central1-DEV-PROJECT-NAME.cloudfunctions.net or https://us-central1-PROD-PROJECT-NAME.cloudfunctions.net) for the project that the SPA is deployed to?
eg. is there some global I can access in the frontend js code where I could do something like:
const url = `${__FIREBASE_GLOBALS__.cloudFunctions.baseUrl}/test/hello/${input}`;
And have the url be correctly defined based on which project the hosted app is deployed to?
I'm assuming here that you're not using Firebase in any other way in your SPA other than to call Cloud Functions (since you didn't say otherwise).
Read the Firebase web setup docs for Firebase Hosting, especially the section on SDK imports and implicit initialization. When you host a site with Firebase Hosting, there are some special URLs that give you the configurations for that project. There are some special script includes that give you access to Firebase products. In particular, note the relative path URI /__/firebase/init.js will yield JavaScript that initializes the Firebase JavaScript SDK with the default settings for your project. Go ahead and access that in a browser pointing to your web app. You're probably interested in the projectId property of the config.
If you want to get a hold of that value, you can use the Firebase SDK, which would be initialized by the script includes from the first link above. Minimally, you could add:
<script src="/__/firebase/5.8.2/firebase-app.js"></script>
<script src="/__/firebase/init.js"></script>
Then later on (see API docs):
firebase.app().options.projectId
to get the ID of the project where Firebase Hosting is serving the content. You can use that to build the URL to your functions.
It might also be convenient for you to port your HTTP functions to callable functions and invoke them from the web site with the Firebase SDK to invoke kthem. Or not.
I was able to get the region and appId from the environment variables.
eg:
console.log(process.env);
Check your firebase logs
{ ...
ENTRY_POINT: 'server',
X_GOOGLE_FUNCTION_TRIGGER_TYPE: 'HTTP_TRIGGER',
FIREBASE_CONFIG: '{"projectId":"pid","databaseURL":"https://pid.firebaseio.com","storageBucket":"pid.appspot.com","locationId":"europe-west"}',
X_GOOGLE_FUNCTION_NAME: 'server',
FUNCTION_TRIGGER_TYPE: 'HTTP_TRIGGER',
X_GOOGLE_GCLOUD_PROJECT: 'pid',
FUNCTION_NAME: 'server',
X_GOOGLE_GCP_PROJECT: 'pid',
X_GOOGLE_FUNCTION_REGION: 'us-central1',
FUNCTION_REGION: 'us-central1',
X_GOOGLE_ENTRY_POINT: 'server',
GCLOUD_PROJECT: 'pid',
GCP_PROJECT: 'pid',
... ommited
}
Out of these GCP_PROJECT, GCLOUD_PROJECT, FUNCTION_REGION, FUNCTION_NAME should work. So for eg. process.env.FUNCTION_REGION
Not sure how reliable this will be.

Accessing firebase hosting files from firebase function triggers

I have 2 repos one for firebase functions and another for static firebase hosting react site. They both use same firebase project.
myfirebaseproject
--- firebase functions
--- firebase hosting
------index.html
Can I read my firebase-hosting files from firebase-function?
let indexHTML = fs.readFileSync('pathToFirebaseHosting/index.html').toString();
One important thing to realize is that Firebase Hosting and Cloud Functions are completely different products, and there is not a simple local path for Hosting files available in a very temporary and transient Cloud Functions instance. Everything is not bundled up together in one place (that wouldn't scale at all).
Since Hosting files are all public, what Frank says is right - just make an HTTP request for what you need. I suggest using the request-promise module instead, as it gives you a promise-based interface that's easier to work with in Cloud Functions.
Please note that if you want to make an outgoing HTTP request like this, you will have to enable billing on your project. Otherwise, Cloud Functions will not allow the request to happen. Also note that the egress and ingress from both Cloud Functions and Hosting will have additional billing. So if you make this request frequently, expect some costs associated with it.
If you just want some static files available in Cloud Function without having to make an external request, simply copy those files in to your functions folder and deploy them all along with your code. You will be able to read them locally, you won't need to enable billing to try it, and you won't incur egress and ingress charges. It'll also be faster.
While there is a REST API for Firebase Hosting API, it is focused on deploying updates, and does not have a call to access the deployed files.
But since all deployed files are publicly accessible, you can read them from the public internet with a regular HTTP request:
request('https://yourproject.firebaseapp.com/index.html', (err, res, body) => {
if (err) { return console.log(err); }
console.log(body);
});

How to reference Firebase Functions config variables from a Firebase-hosted application?

I am transitioning a Heroku-hosted ReactJS/NodeJS application to be hosted on Firebase. Because Firebase only handles static pages I need to reconfigure how my private environmental-specific variables for Development, Staging, Production environments are configured. For example before I defined these sorts of variables:
CLIENT_ID=secret_here
DOMAIN=secret_here
REDIRECT_URI=secret_here
upon the Heroku environment I was deploying to and now I must set them into firebase functions environments from the Firebase CLI for Dev, Staging and Production.
Firebase has documentation on adding environmental configurations to Firebase Functions such that I can add key/value pairs to be accessed at runtime from within a firebase function:
firebase functions:config:set mySecret.key="CLIENT_ID" mySecret.id="secret_here"
however I am unclear how added configuration variables can be accessed from Firebase-hosted static applications (rather than functions).
Is it as simple as simply referencing the firebase functions library from my application and retrieving the defined key from within my application (like so)?
const functions = require('firebase-functions');
...
functions.config().auth0.CLIENT_ID
The variables you define through the link you provided are only available via Cloud Functions. They're not made available directly to the static content served by Firebase Hosting.
If you want, you can make an HTTP function that does nothing but return the JSON from your env vars, and call that from your web content. Bear in mind that you're exposing your secret keys to the world.
The best practice way to handle what I am trying to do is to refactor all of the application logic requiring environment configuration into firebase functions. I can then invoke said functions to reference environment config and execute the code consuming them. This is the answer/solution I needed and didn't have the context to realize earlier. thanks for edging me towards that realization.

Resources