I have a simple express app like this.
const express = require("express");
var app = express();
app.get("/", (req, res)=>{
...handle request...
});
...
exports.app = functions.https.onRequest(app);
I am deploying on firebase. and when I deploy it it creates a new route by the name of the function like this: https://us-central1-[projectname].cloudfunctions.net/app
this is because I put exports.app = functions.https.onRequest(app);
so how can I deploy and make it work without the /app at the end? I need this because I am having issue with the references on my front end which use routes like "/login", which in this case won't work since it had added app requiring all routes to be like "/app/login"
I even tried export default app but no luck.
how can I deploy with out the function name as a route?
Removing /[exportname] from the first part of a URL hosted on cloudfunctions.net is not possible as this is how the functions are triggered.
Ideally rather than serve resources from the cloudfunctions.net domain, you place your functions behind Firebase Hosting where you can instead use a URL like https://yourapp.example.com/login which will play nicely with Express.
However, if you wish to call https://us-central1-[projectname].cloudfunctions.net/app/login and have it behave as if it was called from https://us-central1-[projectname].cloudfunctions.net/login, you can make use of a conditional URL rewrite. The example below will strip /app from the URL if-and-only-if the hostname ends in cloudfunctions.net and the URL also starts with "/app", then handing over to the other routes.
import express from "express";
function removePathForCloudFunctionsDomain(path) {
return function(req, res, next) {
const rawUrl = req.url; // stash original URL
// do nothing if not on cloudfunctions.net or path doesn't match
if (!req.hostname.endsWith("cloudfunctions.net") || !rawUrl.startsWith(path)) {
return next();
}
// if here, trim path off of the request's URL
req.url = req.originalUrl = rawUrl.slice(path.length);
// hand over to other app.get(), app.use(), etc.
next('route');
}
}
const app = express();
app.use(removePathForCloudFunctionsDomain("/app"));
/* other routes */
export app;
Related
I have a question related to the static generation of Next.js:
I'm creating whitelabel websites for my customers; it means that I'm reading the domain where the request is coming from to load a config file and some specific CSS files. That's working fine and looks like this:
export const readConfig = async ({req}) => {
const configs = await import('../configs.json')
const domain = req ? req.headers['host'].split(':')[0] : window.location.hostname
const config = configs[domain]
return {domain, config}
}
Page.getInitialProps = readConfig
However, I'm using getInitialProps for that and my understanding is that because I rely on req, this code will be loaded for every page.
Now, let's say that I want to have a static generation of some pages, how should I proceed? Can I avoid having count_different_domains * count_different_items combinations? Is that somehow possible to cache the result of some queries and revalidate it later (but not as entire Page)?
i followed the sample of authorized-https-endpoint and only added console.log to print the req.cookies, the problem is the cookies are always empty {} I set the cookies using client JS calls and they do save but from some reason, I can't get them on the server side.
here is the full code of index.js, it's exactly the same as the sample:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const express = require('express');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
const app = express();
const validateFirebaseIdToken = (req, res, next) => {
console.log(req.cookies); //// <----- issue this is empty {} why??
next();
};
app.use(cors);
app.use(cookieParser);
app.use(validateFirebaseIdToken);
app.get('/hello', (req, res) => {
res.send(`Hello!!`);
});
exports.app = functions.https.onRequest(app);
store cookie:
curl http://FUNCTION_URL/hello --cookie "__session=bar" // req.cookies =
{__session: bar}
doesn't store:
curl http://FUNCTION_URL/hello --cookie "foo=bar" // req.cookies =
{}
If you are using Firebase Hosting + Cloud Functions, __session is the only cookie you can store, by design. This is necessary for us to be able to efficiently cache content on the CDN -- we strip all cookies from the request other than __session. This should be documented but doesn't appear to be (oops!). We'll update documentation to reflect this limitation.
Also, you need to set Cache-Control Header as private
res.setHeader('Cache-Control', 'private');
Wow this cost me 2 days of debugging. It is documented (under Hosting > Serve dynamic content and host microservices > Manage cache behavior, but not in a place that I found to be useful -- it is at the very bottom "Using Cookies"). The sample code on Manage Session Cookies they provide uses the cookie name session instead of __session which, in my case, is what caused this problem for me.
Not sure if this is specific to Express.js served via cloud functions only, but that was my use case. The most frustrating part was that when testing locally using firebase serve caching doesn't factor in so it worked just fine.
Instead of trying req.cookies, use req.headers.cookie. You will have to handle the cookie string manually, but at least you don't need to implement express cookie parser, if that's a problem to you.
Is the above answer and naming convention still valid? I can't seem to pass any cookie, to include a session cookie named "__session", to a cloud function.
I setup a simple test function, with the proper firebase rewrite rules:
export const test = functions.https.onRequest((request, response) => {
if (request.cookies) {
response.status(200).send(`cookies: ${request.cookies}`);
} else {
response.status(200).send('no cookies');
}
});
The function gets called every time I access https://www.xxxcustomdomainxxx.com/test, but request.cookies is always undefined and thus 'no cookies' is returned.
For example, the following always returns 'no cookies':
curl https://www.xxxcustomdomainxxx.com/test --cookie "__session=testing"
I get the same behavior using the browser, even after verifying a session cookie named __session was properly set via my authentication endpoint. Further, the link cited above (https://firebase.google.com/docs/hosting/functions#using_cookies) no longer specifies anything about cookies or naming conventions.
How can I properly simulate a cloud function locally so that it has all data as when being invoked on firebase servers? (e.g. the context.auth)
I am serving my project with firebase serve, it runs ok on http://localhost:5000/, however, my cloud functions are being called from https://us-central1-<my-app>.cloudfunctions.net/getUser. (The function is not even deployed.)
To avoid XY problem, I am trying to debug my function, but calling it from firebase shell results in context.auth being undefined, same when calling via postman from http://localhost:5000/<my-app>/us-central1/getUser.
This is my ./functions/src/index.ts file
import * as functions from 'firebase-functions'
import admin from 'firebase-admin'
import { inspect } from 'util'
admin.initializeApp()
export const getUser = functions.https.onCall((data, context) => {
console.debug('== getUser called =========================================')
console.log('getUser', inspect(data), inspect(context.auth))
return admin.database().ref('userRights/admin').child(context.auth.uid).once('value', snapshot => {
console.log(snapshot.val())
if (snapshot.val() === true) {
return 'OK'
// return {status: 'OK'}
} else {
return 'NOK'
// return {status: 'error', code: 401, message: 'Unauthorized'}
}
})
})
file ./firebase.functions.ts
import { functions } from '~/firebase'
export const getUser = functions.httpsCallable('getUser')
Consumer ./src/pages/AdminPanel/index.tsx
import { getUser } from '~/firebase.functions'
//...
getUser({myDataX: 'asd'}).then(response => console.debug('response', response))
UPDATE - April/2021
As of April/2021, method useFunctionsEmulator has been deprecated. It is suggested to use method useEmulator(host, port) instead.
Original post:
By default, firebase serve sends queries to CLOUD function instead of localhost, but it is possible to change it to to point to localhost.
#gregbkr found a workaround for that at this github thread.
You basically add this after firebase initialization script (firebase/init.js) in html head.
<script>
firebase.functions().useFunctionsEmulator("http://localhost:5001");
</script>
Make sure to REMOVE it when deploying to SERVER
There is currently no support for local testing of callable functions like this. The team is working on a way for you to specify the URL endpoint of a callable function so that you can redirect it to a different location for testing.
Just found a workaround.
using fiddlers AutoResponder to redirect the function call to the local served function.
step 1
copy the target url of the function from the client
step 2
copy the local served function url
step 3
active the auto respoder and use the following rules
(the second rule is also importent to allow all outher requests
That worked for me, thank you #GorvGoyl!
script src="/__/firebase/init.js?useEmulator=true"></script
Is there a way of defining the HTTP path (after the first '/') to access a Cloud Function for Firebase?
What I'm tying to achieve is to create a rest-like path system to access the functions.
I have a GitHub with my project if there is any doubts.
The cloudfunctions.net domain will route all traffic beginning with a function name to that function. So, for example, you could do this with a standard Express app:
var functions = require('firebase-functions');
var express = require('express');
var app = express();
app.post('/bar', (req, res) => {
res.end('bar');
});
app.get('/foo', (req, res) => {
res.end('foo');
});
exports.myFunc = functions.https.onRequest(app);
The above will allow you to make requests to /myFunc/foo and /myFunc/bar and handle them separately. One thing to note is that currently if you pass an Express app there will be an error if you try to access your function at /myFunc, instead needing to make your request to /myFunc/ (with a trailing slash).
I have created Firebase Cloud Functions app,
I created function with https.onRequest.
and get data with req.body but there is not data there.
Can Firebase Cloud Functions can handle HTTP POST method?
This is my sample code:-
var functions = require('firebase-functions');
exports.testPost = functions.https.onRequest((req, res) => {
console.log(req.body);
});
I tested by postman with POST method but didn't show result in Firebase log.
Functions built on Firebase can also use Express.js routers for handling GET/POST/PUT/DELETE, etc... is fully supported by Google, and is the recommended way to implement these types of functions.
More documentation can be found here:
https://firebase.google.com/docs/functions/http-events
Here's a working example built on Node.js
const functions = require('firebase-functions');
const express = require('express');
const cors = require('cors');
const app = express();
// Automatically allow cross-origin requests
app.use(cors({ origin: true }));
app.get('/hello', (req, res) => {
res.end("Received GET request!");
});
app.post('/hello', (req, res) => {
res.end("Received POST request!");
});
// Expose Express API as a single Cloud Function:
exports.widgets = functions.https.onRequest(app);
Then, run firebase deploy, and that should compile your code and create the new "widgets" function. Note: You can rename widgets to anything you want. Ultimately, it will generate a URL for calling the function.
I am planning to do the same thing. What I reckon the approach should be is to check the request.method in the function body. A probable approach can be:
if (request.method != "POST") {
respond.status(400).send("I am not happy");
return;
}
// handle the post request
Here's some reference to the details regarding what the request object holds: https://firebase.google.com/docs/functions/http-events
Firebase functions support GET, POST, PUT, DELETE, and OPTIONS method, and you can check what kind of methods that trigger your function.
// Check for POST request
if(request.method !== "POST"){
res.status(400).send('Please send a POST request');
return;
}
Then to get data from POST request (for example JSON type) will be in the header of your request.
const postData = request.body;
// for instance
const format = req.body.format;
// query string params
let format = req.query.format;
Maybe your project hasn't been setup to communicate with your firebase database. Try the following from your terminal:
npm install -g firebase-tools
Then inside your project folder, run the following and login using your credentials
firebase login
Then
firebase init functions
This will create a folder with index.js, package.json and node_modules
If you are using Postman correctly the rest of your code should work.