Firebase Functions: route every URL through function and serve from "hosting" - firebase

I'm trying to get every url in a subdomain to be routed through a firebase function. This is my configuration:
The functions file:
const functions = require('firebase-functions');
exports.handleWebRequest = functions
.region('europe-west1')
.https
.onRequest((req, res) => {
res.send('Currently down.');
});
My firebase.json:
{
"hosting": {
"public": "dist",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"function": "handleWebRequest"
}
]
},
"functions": {
"source": "firebase-functions",
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
}
}
When I deploy the hosting and functions, and go to the URL in my browser, I expect to see "Currently down" for every route I open. However, every route except the root route shows the "Currently down" message, and the root route shows the index.html that I deployed.
So, in a nutshell:
/ shows my index.html (which I don't want)
/whatever shows "Currently down." (which is what I want)
The reason I'm trying to route everything through the function, is because I want to shield the website with HTTP Basic Auth, which is "not natively supported" by Firebase.

This is most probably because you still have the index.html file under the public folder.
As explained in the doc:
Note: The static files in the public directory take precedence over
the rewrites, so any static files will be served alongside the Cloud
Functions endpoints.
If you remove the index.html file it should work the way you expect.

Your function is deployed to the "europe-west1" region, but Firebase Hosting currently only supports the default "us-central1". This is stated in the documentation:
Important: Firebase Hosting supports Cloud Functions in us-central1 only.

Related

Firebase Functions multiple directories - Nuxt 3 / Nitro

I'm experimenting with Nuxt 3 on Firebase hosting and have a basic build deployed successfully. However, it has taken over the functions directory.
Is it possible to specify a second directory in firebase.json? Thanks.
Here is the relevant portion of my working-for-nuxt firebase.json file.
"functions": {
"source": ".output/server",
"runtime": "nodejs14",
"ignore": [
"node_modules",
".git",
"firebase-debug.log",
"firebase-debug.*.log"
]
},
Nuxt 3 creates a single function ("server") and deploys to Cloud Functions. If you want to deploy functions from another source directory as well e.g. for Firestore triggers then you can pass an array to functions property in the firebase.json as shown below:
{
"functions": [
{ "source": ".output/server", "codebase": "nuxt_app" },
{
"predeploy": "npm --prefix \"$RESOURCE_DIR\" run build",
"source": "functions",
"codebase": "cloud_functions"
}
]
}
Checkout the documentation to learn more about Managing multiple source packages.
If you are not using background functions like Firestore or Cloud Storage triggers and just using HTTP callable functions, then you can use NuxtJS Server Routes to deploy HTTP endpoints.

Adding root folder outside of public to hosting Firebase

I have the following structure (to handle different theming) using different websites on Firebase.
/src
/theme-1/index.html
/theme-2/index.html
All the "juice" is within src and the file index.html is the same for both theme-1 and theme-2 with exception of the line where I refer to the .css file, where I use actually a different on.
For the moment, I only configured one target but I will add more in my firebase.json:
{
"hosting": {
"target": "theme-1",
"public": "theme-1",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{"source": "**","destination": "/theme-1/index.html"}
],
"cleanUrls": true,
"headers": [ {
"source": "**",
"headers": [ {
"key": "Cache-Control",
"value": "max-age=60"
} ]
} ]
}
}
I then configure my target using the cmd commands to point to my firebase site:
firebase target:apply hosting theme-1 theme-1
and subsequently deploy the website:
firebase deploy
However, when I visit the page (despite it working locally), it seems not to be able to find the root folder /src (404 error).
How can I also include the folder /src in my deployment such that it works in the same way?
Only the directory specified by public is deployed to Firebase. The name is slightly misleading because there are no "private" files/directories.
So you have to do "public": "src" (or "public": "." if you want to deploy the theme directories as well), and adjust the other properties accordingly.

how to handle root path request with node.js on firebase hosting?

I'm developing web system using firebase hosting + functions.
Inspite of specifying rewrite rules on firebase.json,
a part of routing doesn't work.
root/
 ├ functions/
 │ ├index.js
 │ ├routes/
 │ │ └ index.js
 │ └views/
 │ ├ index.jade
 │ └ sub.jade
 └ public/
└index.html // this is created by default. I don't want to use this.
This is my firebase.json
"rewrites": [{
"source": "**",
"function": "app"
}],
And this is node.js code.
router.get('/', function(req, res, next) {
res.render('index');
});
router.get('/subdir', function(req, res, next) {
res.render('sub');
});
result
https://myurl/ -> public/index.html is shown.(not handled on node.js)
https://myurl/ -> handled on node.js
Do you know how to handle root path request with node.js on firebase hosting.
See https://firebase.google.com/docs/hosting/full-config#hosting_priority_order .
Priority order of Hosting responses
The different Firebase Hosting configuration options described on this page can sometimes overlap. If there is a conflict, Hosting determines its response using the following priority order:
Reserved namespaces that begin with a /__/* path segment
Configured redirects
Exact-match static content
Configured rewrites
Custom 404 page
Default 404 page
Exact-match static content is high priority order than Configured rewrites.
So https://myurl/ get static content /index.html.
Can you try remove public/index.html?
I tried. I can rewrite.
See https://github.com/zkohi/firebase-hosting-using-functions-samples .
And you should check https://firebase.google.com/docs/hosting/functions .
Make a dummy folder that is empty. eg.: dummyApiFolder
Make a very simple cloud function. eg.
exports.bla = functions.https.onRequest(async (req, res) => {
res.send("server bla");
});
Use the following hosting rules
"hosting": {
"target": "api",
"public": "dummyApiFolder",
"rewrites": [
{
"source": "**",
"function": "bla"
}
]
}
3.1. target is only needed if you have set this up in your .firebaserc file
3.2. "public": "dummyApiFolder", is needed. Otherwise the CLI won't allow the deployment
Now all requests should get forwarded to bla.
The reason I'm advising to start with a simple cloud function is, express can be setup incorrectly. This can also give you an error, but you end up not knowing if it's a firebase issue or a code issue!
It's simple! Just rename the index.html file in the public directory into some other name. Also, just in case: In the firebase.json file, Change the public property's value
from public to .

Firebase call Google Cloud function issue - two functions with the same name

I've got two Google Cloud functions with the same name but in two different regions.
How do I specify which function should be called (as there are two of them with the same name)?
My firebase.json file:
{
"hosting": {
"public": "public",
"rewrites": [
{
"source": "/test",
"function": "paaspiutil"
}
]
}
}
You can current only deploy functions in us-central1 if you want to link it to hosting as referenced in a previous answer and heres the links to the docs

Best setup/workflow for testing and deploying an application using Google Firebase Hosting and Functions

I built a small web application (www.suntax.de) with vuejs and hosted on Google Firebase. I use the Firebase Hosting, Database and Functions. I use Functions to do some calculation on the backend.
Everything is running now, but I had some trouble getting there and some things I would like to improve regarding my workflow.
Thus, I would like to explain you what I did and where I struggle now.
First I set up the vuejs applicationd deployed it to firebase. I added my custom domain and everything was doing fine.
Later I implemented the cloud function and want to make a call from my vuejs app.
After firebase deploy, I can call this function in the browser and it just works fine:
https://us-central1-suntax-6d0ea.cloudfunctions.net/calcEEGcompensation?year=2013&month=1&size=10
Now I thought, that I just call the URL of the function from my vuejs app. But then I got the following error message:
[Error] Origin * is not allowed by Access-Control-Allow-Origin.
I was then reading that I had to add a rewritesection in the firebase.json:
Now my firebase.json looks like this:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
// Add the following rewrites section *within* "hosting"
"rewrites": [ {
"source": "/calcEEGcompensation", "function": "calcEEGcompensation"
} ]
}
}
Now I was able to call my firebase function with the following URL:
https://www.suntax.de/calcEEGcompensation?year=2013&month=1&size=10
After integrating the above URL in my vuejs application, the application is running fine after deployment to firebase server.
As I want to keep improving the application, I would like to test everything locally before deploying.
I know that I can run firebase hosting and functions locally by:
firebase serve --only functions,hosting
However, now my application has the hard coded call to my function https://www.suntax.de/calcEEGcompensation?year=2013&month=1&size=10 and this again leads to the error [Error] Origin * is not allowed by Access-Control-Allow-Origin.
But also changing the URL to the local function
http://localhost:5001/suntax-6d0ea/us-central1/calcEEGcompensation?year=2013&month=1&size=10
leads to the error message
[Error] Origin * is not allowed by Access-Control-Allow-Origin.
Some further reading brought me to the solution with cors. So I changed my function to:
const functions = require('firebase-functions');
const cors = require('cors')({origin: true});
exports.calcEEGcompensation = functions.https.onRequest((req, res) => {
cors(req, res, () => {
const yearOfCommissioning = req.query.year;
const monthOfCommissioning = req.query.month;
const pvsystemsize = req.query.size;
...
res.send("hello");
});
});
This helped and everything works now:
- The deployed application is still running fine.
- I can run the application locally while calling the local function as well as the deployed function. I just have to change the URL of the function.
But this is now my question:
Can I solve this issue in a better way? If I test the vuejs application and the function locally, I have to change the function URL before deployment. And then I have to change it back while testing locally.
I was not able to test my application and function locally without cors.
My ideal solution would be to have a setup, that can be fully tested locally and which can be easily deployed with firebase deploy without any changes of URLs. Is this possible?
Thanks and best regards,
Christoph
I found the solution which is pretty simple and does exactly what I want. I have no idea why I did not figure it out before.
Here is what I did:
I just call the relative URL from my firebase hosting:
calcEEGcompensation?year=2013&month=1&size=10
and everything works fine if the rewrites are properly set in firebase.json:
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
// Add the following rewrites section *within* "hosting"
"rewrites": [ {
"source": "/calcEEGcompensation", "function": "calcEEGcompensation"
} ]
}
}
After setting up everything like this, I can just execute firebase serve --only functions,hosting and I can test everything locally.
After executing firebase deploy, everything runs smoothly on the server.
I do not need cors.
Thanks #wonsuc for your answers.
Update(For Firebase Hosting):
Currently there is no workaround you can solve it with Firebase Hosting SDK.
But there is alternative way you can achieve this.
Try below code in your hosting source.
if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
console.log('It's a local server!');
}
In my opinion, these are best way to check dev environment currently.
Therefore you should use location.hostname in your Firebase Hosting, and server.address() in Cloud Functions.
And define your Functions end point with constant variable.
const DEBUG = location.hostname === 'localhost' || location.hostname === '127.0.0.1';
const FUNCTIONS_ENDPOINT_DEV = 'http://localhost:5001/';
const FUNCTIONS_ENDPOINT_PRD = 'https://us-central1-something.cloudfunctions.net/';
const FUNCTIONS_URL_CALC = 'calcEEGcompensation?year=2013&month=1&size=10';
var endpoint;
if (DEBUG) {
endpoint = FUNCTIONS_ENDPOINT_DEV + FUNCTIONS_URL_CALC;
} else {
endpoint = FUNCTIONS_ENDPOINT_PRD + FUNCTIONS_URL_CALC;
}
Original answer(For Cloud Functions for Firebase):
Have you tried node.js net module's server.address() function?
This method will tell you if your functions code is running on localhost or real deployed server.
For examples, you can use like this.
const server = app.listen(function() {
let host = server.address().address;
let port = server.address().port;
if (!host || host === '::') {
host = 'localhost:';
}
console.log('Server is running on %s%s', host, port);
});

Resources