I have an express.js based cloud functions app on firebase in a function named api
. To use a custom domain, I'm trying to use Firebase Hosting rewrites to route the specific URL to the function.
I'm following the official documentation about cloud functions and Firebase hosting here, https://firebase.google.com/docs/hosting/functions, and have tried many combinations including the following:
"rewrites": [
{
"source": "/api/**",
"function": "api"
}
]
"rewrites": [
{
"source": "/api/:path1/:dat1/dat",
"function": "api/:path1/:dat1/dat"
}
]
"rewrites": [
{
"source": "/api/path1/dat1/dat",
"function": "api"
}
]
"rewrites": [
{
"source": "/api/*/*/*",
"function": "api"
}
]
Sadly, It doesn't seem to work for any possible combination. My express app has the following GET paths I plan on using:
'/api/users/:userId/:userData'
'/api/users/:userId/:userData/json'
'/api/users/:userId/'
and others similar to these. the :userId and :userData are the params in my request, as it works with express.js
The required functions work as expected in
https://my-firebase-app.cloudfunctions.net
but they do not work with
https://my-app.firebaseapp.com
Please tell me how these are supposed to work and what I'm doing wrong.
EDIT: Here is a sample of what my cloud functions export looks like
const functions = require('firebase-functions');
const express = require('express');
const app = express();
app.get('/users/:userId/:userData/json', (req, res) => {
// Do App stuff here
}
// A couple more app.get in the same format
exports.api = functions.https.onRequest(app);
EDIT 2: After @DougStevenson's suggestion, I tried the following configuration
I tried the following in my firebase.json ,
{
"hosting": {
"rewrites": [
{
"source": "/api",
"function": "api"
}
],
"public": "public"
}
}
But I got the same problem, the function is never called. I read about how the rewrites are last resort options and if there are files present in the hosting, it will not go to the specified function.(I tried looking for the SO post where this ws mentioned but I can't find it) So I removed the 404.html and index.html files from hosting's public directory, since I don't need them anyway. But the problem still remained.
EDIT 2: Okay, so after a lot of trial and error, I just had to hard code the paths in the following format:
rewrites : [
{
"source": "/users/**/**/json",
"function": "api"
},
{
"source": "/api/users/**/**/json",
"function": "api"
}
]
After this, the express app is configured something like this:
app.get('/users/:userId/:userData/json', Foo)
I still would prefer if someone could suggest a better way to accomplish this rather than manually putting in each required Uri in the hosting rewrites.
What seems to be the main issue is that this:
{
"source": "/api",
"function": "api"
}
is actually rewriting to https://my-firebase-app.cloudfunctions.net/api/api
instead of https://my-firebase-app.cloudfunctions.net/api
like you'd expect. Notice how api
is repeated.
My solution to this is to create a main
function which hosts all other top-level functions:
const functions = require('firebase-functions');
const express = require('express');
const app = express();
app.get('/users/:userId/:userData/json', (req, res) => {
// Do App stuff here
}
// A couple more app.get in the same format
// Create "main" function to host all other top-level functions
const main = express();
main.use('/api', app);
exports.main = functions.https.onRequest(main);
You can now use this main
function to delegate to all other functions without breaking your URL structure:
{
"source": "/api/**", // "**" ensures we include paths such as "/api/users/:userId"
"function": "main"
}
And voila! You can now access all api
function calls via https://my-app.firebaseapp.com/api/users/:userId/:userData
just like you'd expect.
Calling this endpoint, now rewrites to https://my-firebase-app.cloudfunctions.net/main/api
which is technically correct. You can then add more top-level functions by simply adding them to your main
function if you wish:
const hooks = express();
main.use('/hooks/, hooks);