Google Cloud Functions and Firebase Functions

Hi :wave:

I’ve searched for the use of purescript with Google Cloud Functions and Firebase Functions which can be simple nodejs module exporting functions, but have not been able to find anything related, is there any library to wrap Google Cloud Function libs or examples?

Given what they both require is this:

const functions = require('firebase-functions');
exports.foo = functions.https.onRequest((request, response) => {
  // ...
});
exports.bar = functions.https.onRequest((request, response) => {
  // ...
});

I assume the easiest way will be calling purescript from javascript.

but something as simple as:

module Main where 
foo :: GoogleCloudFunction -- < is there a library for this?
foo = ...

foo :: GoogleCloudFunction
foo = ...

should yield the same result?

1 Like

You could write FFI for the functions. Something like the following:

// - FireBase/Functions.js
const functions = require("firebase-functions");
exports.onRequestImpl = functions.https.onRequest;
-- FireBase/Functions.purs
foreign import onRequestImpl :: EffectFn2 Request Response Unit

myFunction :: Request -> Response -> Effect Unit
myFunction = runEffectFn2 onRequestImpl
1 Like

Thanks! I’ll do that right away!

What are your thoughts on resembling the nested nature of the library on FFI?

For example should the import in PS look like Foreign.Firebase.Functions(onRequest) or Foreign.Firebase.Functions.HTTP(onRequest)? Or even dismiss the Foreign

Just asking to know if theres a FFI naming/module convention?

Are PS conventions are written anywhere?

Generally, FFI functions are named as is (e.g. functionName) when they can be used as-is and are otherwise named slightly differently (usually with a “Impl” suffix) when there’s additional work one must do to make it type-safe (e.g. something returns a null and you want to convert it to a Maybe via Nullable).

-- in FFI file: `exports.functionName = functionName;
foreign import functionName :: Object String -> Int

-- vs something like

-- in FFI file: `exports.functionNameImpl = functionName;
foreign import functionNameImpl :: Effect (Nullable String)
functionName :: Effect (Maybe String)
functionName = map toMaybe functionNameImpl

That being said, there are some cases where you might want to use Nullable rather than Maybe.

It might also use the “Impl” suffix if the value is an uncurried function (e.g. EffectFn4 a b c d e). Then one might provide a curried interface by wrapping the function in a runEffectFn4:

-- in JS: `exports.functionNameImpl = functionName;
foreign import functionNameImpl :: EffectFn4 Int Int Int Int Unit
functionName :: Int -> Int -> Int -> Effect Unit
functionName = runEffectFn4 functionNameImpl

I think the first part of a module’s name should follow the library. So, if you publish this as purescript-firebase, the module should be Firebase.Functions (onRequest) and not Foreign.Firebase.Functions (onRequest). If you wanted to distinguish the backend (in case they have bindings to something other than JS and they’re different), then adding a JS somewhere in there would likely work. However, I’m less sure/confident about this because most libraries are written for JS.

1 Like

Thanks! I’m using your approach, so far so good!

As PureScript supports multiple backends and some SDKs/third party libraries do too, I find it extrage PS packages do not specify they are meant for JavaScript and not GO/Swift/etc the only one I’ve seen is js-timers, but in general packages do not care to specify they have only JS bindings.

Whick also makes me think, if I do a package for Firebase which supports multiple languages, should they be multiple PureScript packages each targeting a different backend (purescript-js-firebase-functions. purescript-go-firebase-functions)? or are there directives to target multiple backends and select a differnet FFI implementation?

#IF JavaScript
foreing import functionNameJSImp :: ...
#ENDIF

As I assume the answer is “no, you can’t target multiple backends”, then wonder much more what are the naming conventions to avoid using a JS-only packages.

1 Like

I think anything involving other non-JS backends is still an open topic for discussion. Java was a language that was known for “writing code once, run it everywhere,” but in practice led to “write code once, debug it everywhere” because each OS, at the end of the day, is different in some fundamental ways.

Similarly, we can write code that will work across all backends if no FFI is specified. But then there are some pieces of code that require FFI, so porting these across backends sometimes works and sometimes requires an API change. Regular expressions is one such issue that comes to mind recently.

But, all that being said, I think other backends currently maintain their own fork of the core repos and maintain their own package set (at least, I believe the Erlang backend does). So, I’m not sure how namespacing stuff works in that context.

2 Likes

Thank you for the perspective, I didn’t know of the multiple package sets that seems like a neat solution!

I’ll continue thinking primarily in JS if the time comes worry for other backends :wink: