Things I’ve Learnt Building <em>Firebase Functions Declarations</em>

firebase-functions-declarations is a tool that creates declaration files for a given set of Firebase Functions. When using it, instead of…


This content originally appeared on Things I've Learnt and was authored by Things I've Learnt

firebase-functions-declarations is a tool that creates declaration files for a given set of Firebase Functions.

When using it, instead of writing:

const response = (await firebase.functions().httpsCallable('myFunction')(data)).data

And not knowing what response is what or data should contain, you write:

import { myFunction } from './firebase-functions'

const response = await myFunction(data)

Where response will be typed to be whatever myFunction returns, and data will be type checked, making sure you provide the right type.

How Does It Work

When creating Firebase Functions you’d normally create an index file which imports all function and exports them. This is the entry point for Firebase Functions.

Each function module usually looks something like:

export default functions.https.onCall(async (data, context) {
  // firebase function body...
});

Ideally, my tool would extract the function passed to functions.https.onCall and infer its argument and return types.

This could be done using the TypeScript compiler and other tools (like ts-query or ts-morph), but because I only learnt about these methods after making this tool I went for a different approach. Maybe someday I’ll revisit this tool and introduce these changes, but for now it’s good enough.

Step 1 - Creating the Declarations

My approach was to require the author of the function to introduce the following change the function’s module - in addition to default export-ing the return value of functions.https.onCall, export the callback passed to functions.https.onCall as a function named impl.

For example, the following file is valid for my tool to work:

// ...

export async function impl(data: T, context: functions.https.CallableContext) {
  // do something, return some value of type U
}

export default functions.https.onCall(impl)

Then, using tsc, I can create declaration files for each one of the functions by running:

$ tsc ./functions/src/index.ts --outDir ./src/firebase-functions --emitDeclarationOnly --declaration

Now src/firebase-functions should contain .d.ts files for all modules within functions/src.

Step 2 - Creating The “Proxy” Module

Now that I have declarations files for the API, I can create a module that:

  • Imports firebase from firebase/app, since this file will eventually make the call to firebase.functions().httpsCallable.
  • Imports all functions. Here’s one import statement for example:
import { impl as myFunctionImpl } from './functions/my-function'

Note: Since I ran tsc emitting declaration files only, functions/my-function.js does not exist, but functions/my-function.d.ts does and that’s all I need.

  • Per each function, exports a function with the same name, that:

    • Accepts a data argument who’s type is inferred from the relevant .d.ts file
    • Returns a promise that resolves to what the function returns

This can be achieved using TypeScript’s Parameters and ReturnType utility types.

Here’s an exported function for example:

export async function myFunction(data?: Parameters<typeof myFunctionImpl>[0]) {
  return (await firebase.functions().httpsCallable('myFunction')(data)).data as ReturnType<typeof myFunctionImpl>;
}

And there you have it, a module that exposes typed functions for your API!

Caveats

As I wrote above, this whole making-the-user-do-stuff for this tool to work is not the best approach, especially since this work can be automated. But, the counter approach of making-me-do-stuff-for-the-user is also not the best one since I’m lazy.

But that’s not what I wanted to raise, the bigger problem with such a tool is that when the types exposed in a function’s signature are ones declared within the Firebase Functions package’s node_modules. In this case there is no way no simple way to share types betweens the functions and app’s codebase.

I made some pretty big projects so far using Firebase and haven’t encountered this issue. But I can image some pretty trivial use cases where this might be a deal-breaker.


This content originally appeared on Things I've Learnt and was authored by Things I've Learnt


Print Share Comment Cite Upload Translate Updates
APA

Things I've Learnt | Sciencx (2020-01-04T22:48:39+00:00) Things I’ve Learnt Building <em>Firebase Functions Declarations</em>. Retrieved from https://www.scien.cx/2020/01/04/things-ive-learnt-building-emfirebase-functions-declarations-em/

MLA
" » Things I’ve Learnt Building <em>Firebase Functions Declarations</em>." Things I've Learnt | Sciencx - Saturday January 4, 2020, https://www.scien.cx/2020/01/04/things-ive-learnt-building-emfirebase-functions-declarations-em/
HARVARD
Things I've Learnt | Sciencx Saturday January 4, 2020 » Things I’ve Learnt Building <em>Firebase Functions Declarations</em>., viewed ,<https://www.scien.cx/2020/01/04/things-ive-learnt-building-emfirebase-functions-declarations-em/>
VANCOUVER
Things I've Learnt | Sciencx - » Things I’ve Learnt Building <em>Firebase Functions Declarations</em>. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2020/01/04/things-ive-learnt-building-emfirebase-functions-declarations-em/
CHICAGO
" » Things I’ve Learnt Building <em>Firebase Functions Declarations</em>." Things I've Learnt | Sciencx - Accessed . https://www.scien.cx/2020/01/04/things-ive-learnt-building-emfirebase-functions-declarations-em/
IEEE
" » Things I’ve Learnt Building <em>Firebase Functions Declarations</em>." Things I've Learnt | Sciencx [Online]. Available: https://www.scien.cx/2020/01/04/things-ive-learnt-building-emfirebase-functions-declarations-em/. [Accessed: ]
rf:citation
» Things I’ve Learnt Building <em>Firebase Functions Declarations</em> | Things I've Learnt | Sciencx | https://www.scien.cx/2020/01/04/things-ive-learnt-building-emfirebase-functions-declarations-em/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.