Cloud Functions
Firenook runs your Cloud Functions code in its own runtime, one Node worker per codebase, and delivers HTTP requests and events from the other emulators to it. Read this page to see which triggers it delivers, how it loads and reloads your code, and how to call any function by hand.
Set it up
Section titled “Set it up”Functions start when firebase.json has a functions section (or
extensions, which run on the same runtime):
{ "functions": { "source": "functions", "codebase": "default", "ignore": ["node_modules", ".git", "firebase-debug.log", "*.local"] }, "emulators": { "functions": { "port": 5001 } }}functions can also be an array of codebases; each one gets its own worker.
Firenook finds your functions through the firebase-functions SDK already in
the codebase’s node_modules, as the official CLI does, so install your
dependencies first (cd functions && npm install). Nothing else is needed: no
firebase-tools and no Java.
Only JavaScript and TypeScript compiled to JavaScript run; Python and Dart
functions do not. For TypeScript, --build compiles your code before it
loads and again on every save (TypeScript functions).
Client apps reach callable functions with
connectFunctionsEmulator(functions, '127.0.0.1', 5001), and HTTP functions
at http://127.0.0.1:5001/<project>/<region>/<name>.
Node 22, 24 or 26
Section titled “Node 22, 24 or 26”Your functions run on the Node that runs Firenook, as the official emulator runs them on its own Node. Firenook is tested on Node 22, 24 and 26:
-
On Node 22, 24 or 26 the emulators start.
-
On an older Node,
emulators:startandemulators:execstop at once and say how to switch:Firenook: the emulators need Node 22, 24 or 26, since your Functions run on it; this is Node 20.20.2. Switch to Node 24 (with nvm: nvm install 24) and run it again -
On an odd or a newer major (23, 25, 27 and up), the emulators start with a warning, since nobody has tested that Node.
A codebase says which Node it deploys on with engines.node in its
package.json, or runtime (such as nodejs22) on its entry in
firebase.json, which comes first. When the Node running the emulators is
another one, the start says so, as the official emulator does, and goes on:
firenook functions: functions/package.json asks for Node 22 in engines.node, but these functions run on Node 24.20.0, the Node running the emulators; start the emulators with Node 22 to run them on the Node they ask for (with nvm: nvm use 22)In a terminal it is one of the start-up warnings, which w lists. A reload
that leaves the codebase asking for the same Node does not repeat it.
Triggers it delivers
Section titled “Triggers it delivers”| Trigger | Delivered |
|---|---|
HTTP (onRequest), callable (onCall) and streaming callable |
Yes |
Firestore (onDocumentCreated, …, and first-generation document.*) |
Yes, when Firestore runs |
Storage (onObjectFinalized, onObjectDeleted, onObjectMetadataUpdated) |
Yes, when Storage runs |
Auth blocking (beforeUserCreated, beforeUserSignedIn) and user.create, user.delete |
Yes, when Auth runs |
Pub/Sub (onMessagePublished) |
Yes, when Pub/Sub runs |
Scheduled (onSchedule) |
Yes, on their schedule (Scheduled functions) |
Task queues (onTaskDispatched) |
Yes (Cloud Tasks) |
Eventarc custom events (onCustomEventPublished) |
Yes (Eventarc) |
| Realtime Database and Firebase Alerts | No: Firenook emulates neither |
A function whose service the suite does not run, or does not emulate, is
loaded and listed but receives no event. The first load says why, with the
official emulator’s line, as a warning on the terminal screen and in the
plain log. Started with --only functions,firestore:
firenook functions[us-central1-onUpload]: function ignored because the storage emulator does not exist or is not running.firenook functions[us-central1-nightlyReport]: function ignored because the pubsub emulator does not exist or is not running.Firenook warns this way for Firestore, Pub/Sub (and so schedules), Storage,
Auth and the Realtime Database; the official emulator warns for Firestore,
Pub/Sub and the Realtime Database only. The console and functions:invoke
still call such a function directly.
HTTP, callable and event functions
Section titled “HTTP, callable and event functions”With the starter helloWorld and a callable addMessage, both answer on the
Functions port:
$ curl http://127.0.0.1:5001/demo-shop/us-central1/helloWorldHello from Firebase!$ curl -X POST -H 'Content-Type: application/json' \ --data '{"data":{"text":"hi"}}' \ http://127.0.0.1:5001/demo-shop/us-central1/addMessage{"result":{"echo":"hi","uid":null}}An event function runs when its service changes. This one writes an audit document for every new order:
const { onDocumentCreated } = require('firebase-functions/firestore');const logger = require('firebase-functions/logger');const { initializeApp } = require('firebase-admin/app');const { getFirestore } = require('firebase-admin/firestore');
initializeApp();
exports.onOrderCreated = onDocumentCreated('orders/{orderId}', async (event) => { const order = event.data.data(); logger.info('order created', { orderId: event.params.orderId, total: order.total }); await getFirestore().doc(`audit/${event.params.orderId}`).set({ seen: true, total: order.total });});Writing orders/o42 runs it, and the log shows the call:
firenook functions: Beginning execution of "us-central1-onOrderCreated"firenook functions: {"orderId":"o42","total":42,"severity":"INFO","message":"order created"}firenook functions: Finished "us-central1-onOrderCreated" in 2.9msInside a worker, the Admin SDK finds every emulator by itself: Firenook sets
FIRESTORE_EMULATOR_HOST, FIREBASE_AUTH_EMULATOR_HOST,
FIREBASE_STORAGE_EMULATOR_HOST, PUBSUB_EMULATOR_HOST,
CLOUD_TASKS_EMULATOR_HOST and CLOUD_EVENTARC_EMULATOR_HOST. The worker
holds demo credentials, never yours: a call that asks for an access token
first, such as an Eventarc publish or a task enqueue, gets the emulators’
token without leaving the machine. Environment variables and
secrets covers your own .env and
.secret.local files, and params with their defaults.
Reload on save
Section titled “Reload on save”Firenook watches each codebase and reloads it when you save. The other emulators keep running, and so does your data:
firenook functions: Loaded functions definitions from source: helloWorld, addMessage, onOrderCreated, onUpload, nightlyReport, sendReceipt, onShipped.firenook functions[us-central1-helloWorld]: http function initialized (http://127.0.0.1:5001/demo-shop/us-central1/helloWorld).firenook functions[us-central1-onShipped]: eventarc function initialized.firenook functions routing refreshed: 7 registered functionsTrimmed: every function gets its initialized line. Functions that do not
load leave every emulator running, the version loaded before keeps
answering, and the next save loads them again; if a save seems to change
nothing, read
the terminal screen or the console. In a terminal, r reloads by hand.
In 0.2.0, a function you remove, rename or move to another region (a
region param changed in .env, for one) keeps its old route until the
emulators restart. A call to the old route answers 500, and the log says:
firenook functions[us-central1-params]: the source no longer exports this function (record kept from an earlier load)Restart the emulators after you change a function’s name or region.
Debugging and reload on save covers
--inspect-functions and attaching a debugger.
Call any function with functions:invoke
Section titled “Call any function with functions:invoke”firenook functions:invoke calls a function on the running suite. Without
--event-data it sends an HTTP request, or a callable body with --data:
$ npx firenook functions:invoke addMessage --data '{"text":"hi"}'200 OK http://127.0.0.1:5001/demo-shop/us-central1/addMessage{"result":{"echo":"hi","uid":null}}With --event-data it builds the event the official functions:shell would
and delivers it, for any background trigger:
$ npx firenook functions:invoke onOrderCreated --resource orders/o99 \ --event-data '{"status":"paid","total":99}'Firenook: firestore event google.cloud.firestore.document.v1.created for onOrderCreated (us-central1, gcfv2) via trigger key us-central1-onOrderCreated-0200 OK http://127.0.0.1:5001/functions/projects/demo-shop/triggers/us-central1-onOrderCreated-0{"status":"acknowledged"}A Pub/Sub event is published through the Pub/Sub emulator, so every
subscriber sees it, and a task goes through the Cloud Tasks emulator, so
retries and rate limits apply. --auth fills the event’s auth, --params
fills path wildcards, --event-type sets an Eventarc type, and --json
prints the whole record. CLI commands lists every flag.
functions:shell itself does not exist: Firenook never starts a second
Functions runtime.
Every call, kept
Section titled “Every call, kept”Firenook keeps each invocation with the request or event that caused it, its answer, its timing and the log lines it printed: the last 2,000 calls, at most 64 MiB. The console’s Functions page shows them, and your coding agent reads them through MCP, with the error and stack trace of a call that failed.
What it does not do
Section titled “What it does not do”- No network sandbox. A
demo-project never reaches a real Firebase project, and workers get demo credentials, never yours, but your code can still call any external API it is written to call. - No Secret Manager. Put secret values in
.secret.local. - No Python or Dart runtimes.
- Headers named
x-firenook-*are the engine’s own and are removed before your function sees a request.
- Scheduled functions
- Environment variables and secrets
- TypeScript functions
- Debugging and reload on save
- Extensions, which run on the same runtime