Migrate Cloud Functions
Move Firebase Cloud Functions or standalone Functions Framework handlers onto Nimbus today. This migration keeps source imports unchanged for the covered surface. Nimbus does not add a second handler API.
This guide covers two audiences:
- Firebase v2 authors using
firebase-functions/v2/* - Standalone authors using
@google-cloud/functions-framework
Keep the compatibility matrix open as the precise support reference.
1. Keep your handler modules and imports
Section titled “1. Keep your handler modules and imports”These imports stay unchanged for the covered surface:
Firebase v2:
firebase-functions/v2firebase-functions/v2/firestorefirebase-functions/v2/https- covered
firebase-admin/app - covered
firebase-admin/firestore
Standalone Functions Framework:
@google-cloud/functions-framework- covered
firebase-admin/app - covered
firebase-admin/firestore
2. Check your project layout
Section titled “2. Check your project layout”Firebase project: Nimbus preserves the conventional layout, including
firebase.json functions.source and multi-codebase layouts:
my-app/ firebase.json functions/ package.json src/ index.tsStandalone Functions Framework package:
my-functions/ package.json src/ index.ts .nimbus/ firebase/ targets.jsonStandalone packages require a hand-authored
.nimbus/firebase/targets.json, because functions.cloudEvent() and
functions.http() name targets without carrying their Firestore or HTTP
binding metadata in source:
{ "version": 1, "targets": [ { "name": "helloWorld", "entrypoint": "registry.helloWorld", "authoring_surface": "functions_framework", "signature_type": "http", "binding": { "binding_kind": "https", "exposure": "http", "path": "/hello", "execution": "request_principal" } }, { "name": "syncUser", "entrypoint": "registry.syncUser", "authoring_surface": "functions_framework", "signature_type": "cloud_event", "binding": { "binding_kind": "firestore_document", "event_type": "google.cloud.firestore.document.v1.written", "database": "(default)", "document": "users/{userId}", "execution": "service_account" } } ]}Firestore document bindings must use "execution": "service_account".
3. Generate artifacts
Section titled “3. Generate artifacts”From the project root:
nimbus codegenGenerated outputs land under .nimbus/firebase/: artifact.json,
targets.json, bundle.mjs, and bundle.sha256.
4. Run locally
Section titled “4. Run locally”For the watched dev loop with app-root auto-detection (nimbus dev walks
up from the current directory, bounded by the nearest .git):
nimbus devFor production-shaped startup, pass the app path explicitly. The nimbus start command does not walk up the directory tree. If the artifact contains
onRequest, onCall, or Functions Framework HTTP targets, bind that
deployment to an existing Nimbus tenant:
NIMBUS_CLOUD_FUNCTIONS_TENANT=app-production nimbus start --app-dir .In a monorepo where the functions package is not at the root:
NIMBUS_CLOUD_FUNCTIONS_TENANT=app-production \ nimbus start --app-dir ./packages/functionsThe binding is server configuration, not part of the request URL. It remains
fixed when you deploy a new Cloud Functions artifact, so adding more tenants
to the same Nimbus server cannot redirect an HTTP handler. Startup and deploy
both refuse HTTP targets when you omit the binding. nimbus dev binds the
detected app to its auto-provisioned development tenant.
5. Know the HTTP path rules
Section titled “5. Know the HTTP path rules”| Surface | Path rule |
|---|---|
functions.http(name, handler) | Comes from targets.json |
Firebase onRequest | /<exportName> |
Firebase onCall | /<exportName> |
For example, Nimbus serves export const hello = onRequest(...) at /hello
on the main server port.
6. Write for at-least-once delivery
Section titled “6. Write for at-least-once delivery”Firestore triggers on Nimbus use durable at-least-once delivery, journal-backed crash/restart replay, bounded retry, service-principal execution, and chain-depth limiting. Practically:
- make handler code idempotent
- expect Nimbus to retry follow-up writes
- expect Nimbus to bound recursive trigger chains
7. Stay inside the covered firebase-admin slice
Section titled “7. Stay inside the covered firebase-admin slice”The covered admin surface for handler bodies is:
initializeApp(),getApp(),getApps(),deleteApp()getFirestore()collection(path),doc(path)get(),set(),update(),delete()DocumentSnapshot.data(),get(fieldPath)- covered
Timestamphelpers
Treat broader Admin SDK usage as unsupported. Unsupported operations fail clearly instead of returning silent stubs.
8. Respect the option boundaries
Section titled “8. Respect the option boundaries”Covered today:
- Firestore document triggers with the documented first-slice options
setGlobalOptions({ retry })for Firestore document triggers- base
onRequest(handler)andonRequest({}, handler)overloads - base
onCall(handler)andonCall({}, handler)overloads
Fail-fast today:
- unsupported
DocumentOptions,HttpsOptions, orCallableOptionsfields - root
onInit() - broader
setGlobalOptions()fields such asregion,memory,serviceAccount, andenforceAppCheck
The covered callable slice includes the Firebase callable JSON envelope,
default CORS behavior, HttpsError / FunctionsErrorCode mapping, and the
unauthenticated baseline. Nimbus defers App Check verification.
9. Deploy
Section titled “9. Deploy”nimbus deploy <server-url> --token <deploy-token>Suggested order
Section titled “Suggested order”- Run
nimbus codegenand confirm that it detects your app root. - Add
targets.jsonfor standalone Functions Framework targets if needed. - Run
nimbus start --app-dir .(ornimbus dev) and verify trigger and HTTP flows locally. - Confirm
firebase-adminusage stays inside the covered slice. - Make handler writes idempotent with at-least-once delivery in mind.
- Deploy with
nimbus deploy.