Cloud Functions for Firebase and Express
Solution 1
This all works because under the covers, an Express app is actually just a function that takes a Node.js HTTP request and response and acts on them with some automatic sugaring such as routing. So you can pass an Express router or app to a Cloud Function handler without issue, because Express's req
and res
objects are compatible with the standard Node.js versions. Basically, it's a "double Express" app where one app is calling another.
As far as function lifecycle and shared state goes: functions are spun up in ephemeral compute instances that may survive to process multiple requests, but may not. You cannot tune or guarantee whether or not a function will be invoked in the same compute instance from one invocation to the next.
You can create resources (such as an Express app) outside of the function invocation and it will be executed when the compute resources are spun up for that function. This will survive as long as the instance does; however, CPU/network are throttled down to effectively zero between invocations, so you can't do any "work" outside of a function invocation's lifecycle. Once the promise resolves (or you've responded to the HTTP request), your compute resources will be clamped down via throttling and may be terminated at any moment.
Solution 2
You shouldn't expect to hold any resource beyond the lifetime of a function. You should expect that the container will completely clean up after the function's final promise has been resolved or the HTTP response has been fully sent. There is no shared state between any function invocations. This is the only way that Cloud Functions can scale. If you need shared state, store that in the database and read it on every invocation, protecting it with a transaction if necessary.
I'm not sure what you're asking in the third question. It kind of seems unrelated to the first two questions - maybe it should be its own question? There is only one endpoint for your backend, as far as I know.
Related videos on Youtube
jthegedus
Development tools of interest: GCP/Firebase Rescript/OCaml Svelte Follow me on: GitHub Twitter LinkedIn Medium "I cannot remember the books I've read any more than the meals I have eaten; even so, they have made me." - Ralph Waldo Emerson
Updated on June 25, 2022Comments
-
jthegedus about 2 years
The Firebase docs for the
functions.https
namespace shows that the function accepts anexpress.Request
object and anexpress.Response
object. Nowhere does it mention that you can pass an express server object tofunctions.https.onRequest
. However, I've found that people have been doing this with no clear indication from commenters that this shouldn't be done (except one person in thefunctions-samples
repo issue #101 thread)see:
-
firebase-functions
https://github.com/firebase/firebase-functions/issues/27 -
functions-samples
for middleware https://github.com/firebase/functions-samples/blob/master/authorized-https-endpoint/functions/index.js
My questions are then:
- How do Cloud Functions for Firebase or GCP Cloud Functions handle the lifetime of objects initialized outside of the function definition?
- How does the above affect the lifetime of the function? Does it run until timeout or function similarly to AWS Lambda?
Clarification for 1 & 2: In Lambda any resources outside of the exported function is used on all subsequent invocations of the same Lambda instance while that function instance is "warm". This means the response time of the function is not negatively affected by any complex initialization code you may have beforehand as it is done once per "warm" instance. In this example, it wouldn't then need to initialize an ExpressJS server each invocation, just once while the function is "warm". I'm curious if Cloud Functions do the same?
Also in Lambda, the existence of the ExpressJS server does not extend the execution time of the function (when it returns it's done), I'm also curious how Cloud Functions is implemented here. Does it simply do the same as Lambda, or (because it may handle existing objects differently) does it do something else?
- The
functions.https.onRequest
documentation doesn't specify you can pass an ExpressJS server object into it, so how is this working? Are there then two endpoints? Can someone explain what is happening here?
Clarification for 3: I've been seeing people do the following:
// './functions/index.js' var functions = require("firebase-functions"); const express = require("express"); // setup ExpressJS Server const expressRouter = new express.Router(); expressRouter.get("*", (req, res) => { res.send(`Hello from Express in Cloud Functions for Firebase`); }); // Cloud Function exports.express = functions.https.onRequest(expressRouter);
And wish to know how this works given the Cloud Functions API only specifies accepting
functions.https.onRequest(request, response)
params modelled after the ExpressJS API.These parameters are based on the Express Request and Response objects - firebase.google.com/docs/functions/http-events
Since all questions pertain to the single snippet of code and this one use case I thought it would be better answered together.
Thanks in advance :)
-
Kato about 7 yearsNote that using middleware is covered briefly in the docs and in functions-samples.
-
Kevin Lee about 7 yearsYour statement "API only specifies accepting functions.https.onRequest(request, response) params modelled after the ExpressJS API." is not entirely accurate. From the Firebase Cloud Functions docs, they actually pass in a function (arrow function), and not just the request and response params.
-
-
jthegedus about 7 yearsThanks for the response, I have updated the post to clarify some points to avoid a long thread here.
-
jthegedus about 7 yearsI'm more than happy to split it into multiple questions though if you still think that would result in a more discoverable q&a
-
Chad Robinson about 7 yearsIt might be fair in more general discussions to say "you should not RELY ON" shared state. Because for clarity, it is possible to HAVE shared state accidentally (which can lead to bugs) if a variable is defined at the wrong level and not properly re-initialized. And there is also the useful pattern for "warm" containers maintaining things like database connections by doing this deliberately...
-
jthegedus about 7 yearsTo clarify further, the expresRouter is only used in the single cloud function definition as in the example code given. I am not sharing this across functions. What I wish to know is that if I do define the express server outside the function, is it and any subsequent DB connections defined, reused in the "warm" function or reinitialized each time. I know AWS Lambda functions do reuse the express object. How are Cloud Functions handled here?
-
jthegedus about 7 yearsThanks for the response. Just a follow-up question: given that all functions are exported through the same index.js file does each function create any resources that are outside of the function definition?
-
Michael Bleigh about 7 yearsYes, each function would be executing the code outside the function definition separately in its own compute infrastructure.
-
Cyupa over 5 years@MichaelBleigh thanks for all your responses on this topic, I've been checking a lot of questions on Cloud Functions on StackOverflow. Do you know if is this ("each function would be executing the code outside the function definition separately in its own compute infrastructure") is true for Google Cloud Functions as well? I read that Firebase Functions are Google Cloud Functions under the hood. I basically want to have a router that handles all CRUD operations via a single cloud function but I want to make sure that each function scales independently.