Cloud Functions for Firebase with custom HTTP path - firebase

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).

Related

firebase remove function name from url

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;

CORS error when making Axios calls to Cloudrun service from Firebase hosted app

This looks to be pretty obvious but I've been trying to figure it out for a couple of days now and still can't get it to work. Please, can someone point out what I'm missing.
I'm trying to make an axios call to my cloud run service from my firebase hosted SPA. To isolate the issue I followed the steps outlined in the [firebase tutorial for cloud run] (https://firebase.google.com/docs/hosting/cloud-run#node.js)
Step 4 of the tutorial talks about setting up rewrite to use the firebase domain as a proxy to your cloud run service. It says that the helloworld service would be reachable via
Your Firebase subdomains:
projectID.web.app/helloworld and projectID.firebaseapp.com/helloworld
So I follow the steps and deploy the service. Then I try to make an axios call to the service from the SPA like below
testHelloWorld () {
axios.get(`https://myProjectId.firebaseapp.com/helloworld`)
.then((data) => {
console.log(data)
})
.catch(ex => {
console.error(ex)
})
})
}
But then I get the CORS error
Access to XMLHttpRequest at 'https://myProjectId.firebaseapp.com/helloworld' from origin 'https://myFirabaseApp.firebaseapp.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This answer states that this should be possible so I'm not sure what I'm doing wrong.
N.B:
While debugging, I updated the node app from the tutorial to add cors support like below. Still didnt work.
const express = require('express');
const app = express();
const cors = require('cors'); //Imported and added this
app.use(cors()); // Used here
app.get('/', (req, res) => {
console.log('Hello world received a request.');
const target = process.env.TARGET || 'World';
res.send(`Hello ${target}!`);
});
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log('Hello world listening on port', port);
});
So the question here is, how do I make an Axios/AJAX call to my cloud run service using the firebase rewrite rule?
Please check if you have installed cors: npm install cors.
Please check if the 2 following options can solve your issue:
1= Use the following code :
app.use(cors({origin:true,credentials: true}));
2) If the previous didn't work, please use the following code:
app.get('/', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
console.log('Hello world received a request.');
}
Please let me know if it works for you.

How to use multiple cookies in Firebase hosting + Cloud Run? [duplicate]

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.

http post request firebase cloud function

I want to make post request and send data into body in firebase cloud function.
as default, it is get request or post request?
var functions = require('firebase-functions');
exports.tryfunction= functions.https.onRequest((req, res) => {
console.log(req.body) // or it should be req.query
});
how do I know and decide what the method it is?
I just sharing simple code part. I'm using like this.
const functions = require('firebase-functions');
exports.postmethod = functions.https.onRequest((request, response) => {
if(request.method !== "POST"){
response.send(405, 'HTTP Method ' +request.method+' not allowed');
}
response.send(request.body);
});
I hope it will be helpful.
There is no default. The request method is whatever the client chose to send.
The req object in your callback is an express.js Request object. Use the linked documentation, you can see that the request method can be found by using req.method.
To specify the HTTP method that your Firebase Function accepts you need to use Express API as you'd normally do in a standard Express app.
You can pass a full Express app to an HTTP function as the argument for onRequest(). This way you can use Express' own API to restrict your Firebase function to a specific method. Here's an example:
const express = require('express');
const app = express();
// Add middleware to authenticate requests or whatever you want here
app.use(myMiddleware);
// build multiple CRUD interfaces:
app.get('/:id', (req, res) => res.send(Widgets.getById(req.params.id)));
app.post('/', (req, res) => res.send(Widgets.create()));
app.put('/:id', (req, res) => res.send(Widgets.update(req.params.id, req.body)));
app.delete('/:id', (req, res) => res.send(Widgets.delete(req.params.id)));
app.get('/', (req, res) => res.send(Widgets.list()));
// Expose Express API as a single Cloud Function:
exports.widgets = functions.https.onRequest(app);
Explanation of the above code:
We expose a Firebase function with endpoint /widgets with different handlers for different HTTP methods using Express' own API, e.g. app.post(..), app.get(..), etc. We then pass app as an argument to functions.https.onRequest(app);. That's it, you're done!
You can even add more paths if you wish, E.g. if we want an endpoint that accepts GET requests to an endpoint that looks like: /widgets/foo/bar, we simply add app.get('/foo/bar', (req, res => { ... });.
It's all taken directly from the official Firebase docs.
I'm surprised #Doug Stevenson didn't mention this in his answer.

How Firebase Cloud functions handle HTTP post method?

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.

Resources