3

Original Question - See Answer at End

We've been using Firebase Functions for 2+ years and have amassed well over 120 HTTP, callable, triggered, and scheduled functions, all being called from a single functions/index and managed by a single package.json, probably like a lot of you. As you can imagine, we have some old dependencies in there that we're hesitant to update because it's an awful lot of code to go through, test, etc. So it got me thinking, and I'm asking if any of you have done this or know why this wouldn't work...

Looking at the GCP dashboard, each function is a separate, stand-alone service. But if you download the code from there, you end up with the full build of all 120+ functions, node modules, etc. So if I run npm deploy on my single functions directory (if quotas weren't an issue), it looks like

  1. Firebase Tools grabs my single build on my machine or CI tool
  2. copies it 120+ times, and then
  3. pushes one full copy of the entire build into each of those functions

That got me thinking - considering I can't and don't want to build my entire project and deploy all functions at once, do I have to have them all in a single functions directory, sharing a single package.json and dependencies, and exported from a single functions/index?

Is there any reason I couldn't have, for example:

- functions

  - functionSingleA (running on node 10)
    - lib/index.js
    - package.json (stripe 8.92 and joi)
    - src/index.ts
    - node_modules

  - functionGroupB (running on node 12)
    - lib/index.js
    - package.json (stripe 8.129 and @hapi/joi)
    - src/index.ts
    - node_modules

I know that I lose the ability to deploy all at once, but I don't have that luxury any more due to quotas. Beyond that, is there any reason this wouldn't work? After all, as best as I can tell, Firebase Functions are just individual serverless Cloud Functions with Firebase credentials built in. Am I missing something, or do you do this and it works fine (or breaks everything)?

Answer from Google Firebase Team

A Firebase engineer through support confirms that this is absolutely possible, but also check out the discussion between me and @samthecodingman. You can break up your functions into completely self-contained modules or groups with different package.json files and dependencies, and deploy each one (individually or as groups) without affecting other functions.

What you lose in return is the ability to deploy all with the firebase functions deploy command (though @samthecodingman presented a solution), and you lose the ability to emulate functions locally. I don't have a workaround for that yet.

I'm Joe Too
  • 5,468
  • 1
  • 17
  • 29

1 Answers1

2

It should be possible by tweaking the file structure to this:

- functionProjects
  - deployAll.sh
  - node10
    - deploy.sh
    - firebase.json
    - functions
      - lib/index.js
      - package.json (stripe 8.92 and joi)
      - src/index.ts
      - node_modules
  - node12
    - deploy.sh
    - firebase.json
    - functions
      - lib/index.js
      - package.json (stripe 8.129 and @hapi/joi)
      - src/index.ts
      - node_modules

As a rough idea, you should be able to use a script to perform targeted deployments. Using the targeted deploy commands, it should leave the other functions untouched (i.e. it won't ask you to delete missing functions).

Each deploy.sh should change the working directory to where it is located, and then execute a targeted deploy command.

#!/bin/bash 
# update current working directory to where the script resides
SCRIPTPATH=$(readlink -f "$0")
SCRIPTPARENT=$(dirname "$SCRIPTPATH")
pushd $SCRIPTPARENT
firebase deploy --only functions:function1InThisFolder,functions:function2InThisFolder,functions:function3InThisFolder,...
popd

The deployAll.sh file just executes each 'child' folder's deploy.sh.

#!/bin/bash
/bin/bash ./node10/deploy.sh
/bin/bash ./node12/deploy.sh

This requires maintaining the list of functions in deploy.sh, but I don't think that's too tall of an ask. You could mock the firebase-functions library so that calls to functions.https.onRequest() (along with the other function exports) just return true and use that to get a dynamic list of functions if you so desire.

You could also flatten the file structure so that ./node10 and ./node12 are the deployed function directories (instead of the nested functions folders) by adding "functions": { { "source": "." } } to their respective firebase.json files.

samthecodingman
  • 23,122
  • 4
  • 30
  • 54
  • So what I’m really giving up is just emulating the full suite at once I think. But beyond that, it seems like you agree that they really are self contained serverless functions. There should be no issue separating them from a dependency standpoint? – I'm Joe Too Jan 02 '21 at 04:14
  • I didn't even consider that you wouldn't be able to emulate them all at once. Not that you would be able to emulate both node10 and node12 functions at the same time anyway. I often have "different builds" running at the same time in one project, you just need to be able identify which build is currently deployed especially if you have functions that share code. – samthecodingman Jan 02 '21 at 04:21
  • Regarding the part where you said it is copied 120+ times, that's the CLI performing validation checks to make sure that each function can be booted correctly before deployment. The last part is where the entire functions directory is uploaded and the functions are linked to that common code deployment. If you use a targeted deployment, the selected functions are switched to that new build, but the untouched ones will still be connected to the old build. – samthecodingman Jan 02 '21 at 04:21
  • Re "copied 120 times" - I get what you're saying, but what I meant was that the whole package is uploaded to each function so it seems like each function could be built separately with its own dependencies so long as it's deployed separately. Which I think we agree is possible so long as I understand what I'm potentially giving up (ie, emulating the full suite, deploying all at once). I've also got a ticket in with Firebase support. If I get an official answer in the next 24, I'll post it. If not, I'll accept. Thanks! – I'm Joe Too Jan 02 '21 at 13:53