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 rout
Another option, if you want both cloudFunction
and hosted
URLS to work is to check if the URL is coming from the hosted URL.
You'd use this function at any time you wanted the params.
export const splitParams = (req: any): string[] => {
let params = req.params[0];
const vals: string[] = [];
// If params starts with a '/' remove it
params = params.startsWith('/')
? params.substr(1, params.length - 1)
: params;
params = params.split('/');
// For hosted URLs the parameters need to be shifted
if (
req.headers['x-forwarded-host'] === 'myURL'
) {
params.shift();
}
for (const param of params) {
if (param !== '') {
vals.push(param);
}
}
return vals;
};
You can use a single Firebase hosting rewrite rule with a complementing rewrite middleware in Express.
Add a rewrite in your firebase.json
file.
{
"source": "/api/**",
"function": "api"
}
Include an app.use() middleware to rewrite the request url.
const functions = require('firebase-functions');
const express = require('express');
const API_PREFIX = 'api';
const app = express();
// Rewrite Firebase hosting requests: /api/:path => /:path
app.use((req, res, next) => {
if (req.url.indexOf(`/${API_PREFIX}/`) === 0) {
req.url = req.url.substring(API_PREFIX.length + 1);
}
next();
});
app.get('/users/:userId/:userData/json', (req, res) => {
// Do App stuff here
});
exports[API_PREFIX] = functions.https.onRequest(app);
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);