As you integrate Cloud Functions into your project, your code could expand to contain many independent functions. You may have too many functions to reasonably fit in a single file, or different teams may deploy different groups of functions, creating a risk of one team overwriting or accidentally deleting another team's functions. Cloud Functions offers different ways to organize your code to make it easier to navigate and maintain your functions.
Organize functions in codebases
You can use the codebase
property of the functions configuration object in
firebase.json
to manage a large collection of functions across multiple
repositories or sub-packages within a single repository monorepo setup:
# firebase.json
"functions": {
"codebase": "my-codebase"
# NOTE: Codebase must be less than 63 characters and can contain only
# lowercase letters, numeric characters, underscores, and dashes.
}
The codebase
property is supported in Firebase CLI v10.7.1 and above.
Managing multiple repositories
The codebase
property can help simplify the management of multiple
repositories. Let's examine a case where you have two different repositories
that deploy functions to the same Firebase project:
$ tree .
├── repoA
│ ├── firebase.json
│ └── functions
│ ├── index.js
│ └── package.json
└── repoB
├── firebase.json
└── functions
├── index.js
└── package.json
Without codebase annotations, the Firebase CLI would have prompted you to delete functions defined in the other repository at the time of deploy:
$ (cd repoA && firebase deploy --only functions)
...
i functions: preparing functions directory for uploading...
✔ functions: functions folder uploaded successfully
The following functions are found in your project but do not exist in your local source code:
fn1FromRepoB
fn2FromRepoB
...
? Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. (y/N)
You can avoid this problem by adding a unique codebase annotation in the
functions configuration section of the firebase.json
in each project repository:
# repoA/firebase.json
"functions": {
"codebase": "repo-a"
}
# repoB/firebase.json
"functions": {
"codebase": "repo-b"
}
With codebase annotation, the Firebase CLI no longer prompts you to delete functions defined outside of your immediate repository:
$ (cd repoA && firebase deploy --only functions)
...
i functions: preparing functions directory for uploading...
✔ functions: functions folder uploaded successfully
# Gleefully ignores functions from repoB
i functions: creating Node.js 16 function fnFromRepoA (us-central1)...
✔ Deploy Complete!
Managing multiple source packages (monorepo)
The codebase
property can help simplify the management of multiple source
packages in a single repository. Let's examine a case where you have a firebase
project directory with function definitions spread across several sub-packages:
$ tree .
├── firebase.json
├── teamA
│ ├── index.js
│ └── package.json
└── teamB
├── index.js
└── package.json
This setup fits the following use cases:
- You have a monorepo setup and have different teams manage their own function definitions in an isolated package.
- You have a function with a heavy external dependency and a long-running initialization, and want to isolate that function from other, latency-sensitive functions.
To support monrepo setup like this, define multiple functions configurations
in firebase.json
:
"functions": [
{
"source": "teamA",
"codebase": "team-a"
},
{
"source": "teamB",
"codebase": "team-b"
},
]
With this configuration, the Firebase CLI deploys functions from all packages in a single deploy command:
$ firebase deploy --only functions
i deploying functions
i functions: preparing codebase team-a for deployment
i functions: preparing codebase team-b for deployment
i functions: creating Node.js 16 function team-a:helloATeam(us-central1)...
i functions: creating Node.js 16 function team-b:helloBTeam(us-central1)...
...
You can also deploy a specific codebase:
$ firebase deploy --only functions:team-b
i deploying functions
i functions: preparing codebase team-b for deployment
i functions: updating Node.js 16 function team-b:helloBTeam(us-central1)...
...
Write functions in multiple files
When getting started with Cloud Functions you might put your first few functions in a single file:
index.js
const functions = require('firebase-functions/v1');
exports.foo = functions.https.onRequest((request, response) => {
// ...
});
exports.bar = functions.https.onRequest((request, response) => {
// ...
});
main.py
from firebase_functions import https_fn
@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
return https_fn.Response("Hello foo!")
@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
return https_fn.Response("Hello bar!")
This can become hard to manage with more than a few functions. Instead, you can put all of your logic for each function in its own file and use your source file as a list of exports:
Node.js
foo.js
const functions = require('firebase-functions/v1'); exports.foo = functions.https.onRequest((request, response) => { // ... });
bar.js
const functions = require('firebase-functions/v1'); exports.bar = functions.https.onRequest((request, response) => { // ... });
index.js
const foo = require('./foo'); const bar = require('./bar'); exports.foo = foo.foo; exports.bar = bar.bar;
Python
foo.py
from firebase_functions import https_fn
@https_fn.on_request()
def foo(req: https_fn.Request) -> https_fn.Response:
return https_fn.Response("Hello foo!")
bar.py
from firebase_functions import https_fn
@https_fn.on_request()
def bar(req: https_fn.Request) -> https_fn.Response:
return https_fn.Response("Hello foo!")
main.py
from fn_impl.foo import *
from fn_impl.bar import *
This setup assumes a project directory structure like the following:
my-project
├── firebase.json
└── functions
├── fn_impl
│ ├── __init__.py
│ ├── foo.py
│ └── bar.py
├── main.py
└── requirements.txt
fn_impl
: Can have any name
__init__.py
: Required, but can be empty
Group functions
In many projects, functions can be separated into logical groups that should be deployed and maintained together. For example, you might have a group of functions used for reporting metrics:
metrics.js
const functions = require('firebase-functions/v1'); exports.usageStats = functions.https.onRequest((request, response) => { // ... }); exports.nightlyReport = functions.https.onRequest((request, response) => { // ... });
You can put these functions into a group when exporting them in your index.js
file:
index.js
// Export both functions from metrics.js in the "metrics" group: // - metrics-usageStats // - metrics-nightlyReport exports.metrics = require('./metrics');
When deployed, functions will be prefixed with the name of their group, so
in this example the functions would be named metrics-usageStats
and metrics-nightlyReport
.
When deploying functions you can limit the action to a single group:
firebase deploy --only functions:metrics
Next steps
To learn more about Cloud Functions, see: