Firebase Generation 2 Cloud Functions

7 min read Web & Mobile Development

Key takeaways

Firebase Cloud Functions Gen 2: How to Start, Ship, and Avoid Pitfalls Gen 2 Cloud Functions (built on Google Cloud Run & Eventarc) give you faster cold starts, higher concurrency, finer resource controls, and broader trigger coverage than Gen 1. This practical guide shows you how to set up a project, write real functions, configure […]

Firebase Cloud Functions Gen 2: How to Start, Ship, and Avoid Pitfalls

Gen 2 Cloud Functions (built on Google Cloud Run & Eventarc) give you faster cold starts, higher concurrency, finer resource controls, and broader trigger coverage than Gen 1. This practical guide shows you how to set up a project, write real functions, configure performance, run locally, and avoid the gotchas teams hit in production.

What’s different in Gen 2 vs Gen 1 (in practice)

  • Runtime/infra: Gen 2 runs on Cloud Run via Eventarc (HTTP and event triggers). You get concurrency, min/max instances, and more regions.
  • Performance controls: Per-function cpu, memory, concurrency, timeoutSeconds, minInstances, maxInstances.
  • APIs & imports: New v2 imports like onRequest (HTTPS), onSchedule (Scheduler), onMessagePublished (Pub/Sub), onObjectFinalized (Storage), etc.
  • Billing reality: Many features (e.g., min instances, some outbound access) expect the Blaze plan. Plan your spend limits.

Install the tooling & create the project

We’ll use TypeScript, the Emulator Suite, and the v2 Functions SDK.

# 1) Install the CLI (global is convenient) npm i -g firebase-tools # 2) Create a fresh folder mkdir fx-gen2-demo && cd fx-gen2-demo # 3) Init Firebase (Functions + Emulator + optionally Firestore/Storage) firebase login firebase init # During init: # - Choose "Functions" (and any other products you need) # - Language: TypeScript # - Use ESLint: Yes # - Use the Emulator Suite: Yes (Functions, Pub/Sub, Storage as needed) # - Use npm to install deps: Yes

If you didn’t let the wizard install, add the packages manually:

npm i firebase-admin firebase-functions npm i -D typescript ts-node @types/node eslint # Optional (helpful) npm i -D vitest tsx @types/jest

Folder layout (what you’ll see)

. ├─ firebase.json # emulators + hosting + functions config ├─ .firebaserc # default project alias └─ functions/ ├─ src/ │ ├─ index.ts # export functions here │ ├─ http/ │ │ └─ hello.ts │ ├─ jobs/ │ │ └─ nightlyReport.ts │ ├─ pubsub/ │ │ └─ onNewOrder.ts │ └─ storage/ │ └─ onUpload.ts ├─ package.json ├─ tsconfig.json └─ .eslintrc.js

Your first Gen 2 HTTPS function (with proper config)

Gen 2 uses v2 imports and exposes per-function settings. Keep HTTP endpoints stateless and idempotent—concurrency means multiple requests share a single instance.

// functions/src/http/hello.ts import { onRequest } from "firebase-functions/v2/https"; import * as logger from "firebase-functions/logger"; // Optional: fine-tune perf per function export const hello = onRequest( { region: "us-central1", memory: "512MiB", cpu: 1, timeoutSeconds: 30, concurrency: 40, minInstances: 0, // set >0 to keep warm (Blaze) maxInstances: 100, invoker: "public", // or ["serviceAccount:xyz@…"] for private }, (req, res) => { logger.info("Hello called", { method: req.method, ua: req.headers["user-agent"] }); res.status(200).json({ ok: true, message: "Hello from Gen 2!" }); } );

Export it from index.ts so Firebase can discover it:

// functions/src/index.ts export { hello } from "./http/hello";

Event-driven examples (Scheduler, Pub/Sub, Storage)

// functions/src/jobs/nightlyReport.ts import { onSchedule } from "firebase-functions/v2/scheduler"; import * as logger from "firebase-functions/logger"; export const nightlyReport = onSchedule( { schedule: "0 3 * * *", // 3am UTC timeZone: "UTC", region: "us-central1", timeoutSeconds: 540, memory: "1GiB", }, async () => { logger.info("Generating nightly report…"); // do the work (call BigQuery, send email, etc.) } ); // functions/src/pubsub/onNewOrder.ts import { onMessagePublished } from "firebase-functions/v2/pubsub"; export const onNewOrder = onMessagePublished( { topic: "orders", region: "us-central1" }, async (event) => { const { message } = event; const data = message.json; // process new order } ); // functions/src/storage/onUpload.ts import { onObjectFinalized } from "firebase-functions/v2/storage"; export const onUpload = onObjectFinalized( { bucket: "your-project.appspot.com", region: "us-central1" }, async (event) => { const { name, contentType, size } = event.data; // e.g., generate thumbnail, validate type, etc. } );

Using secrets & environment config (Gen 2 way)

Prefer Secrets Manager for sensitive values. With the v2 API you can define and access secrets cleanly.

# Create a secret in Google Secret Manager gcloud secrets create STRIPE_API_KEY --replication-policy="automatic" # Add a version (from stdin here for demo) printf "sk_live_xxx" | gcloud secrets versions add STRIPE_API_KEY --data-file=-
// functions/src/http/charge.ts import { onRequest } from "firebase-functions/v2/https"; import { defineSecret } from "firebase-functions/params"; const STRIPE_API_KEY = defineSecret("STRIPE_API_KEY"); export const charge = onRequest({ secrets: [STRIPE_API_KEY] }, async (req, res) => { const key = STRIPE_API_KEY.value(); // safely resolved on the server // Use key to call Stripe SDK… res.json({ ok: true }); });

Local development with the Emulator Suite

Configure emulators in firebase.json and run everything locally—HTTP endpoints, scheduled jobs (manually), Pub/Sub, Firestore/Storage, etc.

{ "emulators": { "functions": { "port": 5001 }, "pubsub": { "port": 8085 }, "storage": { "port": 9199 }, "ui": { "enabled": true, "port": 4000 } } }
# In the repo root firebase emulators:start # Hitting your HTTP function locally curl http://127.0.0.1:5001/PROJECT-ID/us-central1/hello

Deploying (regions, limits, and safety)

# Deploy all functions firebase deploy --only functions # Or deploy a single function firebase deploy --only functions:hello

Region & limits: choose a single region close to users/data (e.g., us-central1) to avoid surprise latencies or cross-region egress. Always set maxInstances if a downstream service can’t handle bursts; Gen 2 scales fast.

Common pitfalls (and how to dodge them)

  • Concurrency foot-guns: With concurrency > 1, multiple requests share a single process. Don’t store per-request state in module-level variables. Use local variables or robust caches with request scoping.
  • Cold starts: Gen 2 cold starts are better, but not zero. If latency SLOs matter, set minInstances > 0 (Blaze). Keep your bundle light and avoid heavy dynamic imports on module load.
  • Long-running work in HTTP: Put the work on a queue (Pub/Sub) and return quickly. For synchronous jobs, raise timeoutSeconds and document client timeouts.
  • IAM & “public”: invoker: "public" is internet-exposed. For internal services, restrict to service accounts or use HTTPS callable functions with auth.
  • Secrets in code: Don’t. Use Secrets Manager via defineSecret. Never log secrets; scrub logs.
  • Region mismatch: Event sources (e.g., Storage bucket) and your function must align or be configured explicitly.
  • Cost spikes: Fast autoscale + unbounded concurrency can DDoS your own database. Cap with maxInstances, use connection pools, and implement retries with backoff.
  • SDK drift: Keep firebase-admin and firebase-functions current; breaking changes sometimes land around Node runtime upgrades.

Recommended function config defaults

// A small helper to keep your config DRY export const defaultConfig = { region: "us-central1", memory: "512MiB" as const, cpu: 1 as const, timeoutSeconds: 60, concurrency: 20, minInstances: 0, maxInstances: 200, };

Import defaultConfig into each function and override only when needed (e.g., image processing might need 2GiB and cpu: 2).

Testing tips (unit & emulator)

  • Pure logic first: extract business logic into plain TS modules and test with Vitest/Jest—fast and stable.
  • Emulator integration: start the emulator from tests or CI; point your Admin SDK to emulator hosts via env vars (FIRESTORE_EMULATOR_HOST, FIREBASE_STORAGE_EMULATOR_HOST, etc.).
  • Disable retries in tests to avoid flakiness; use fixed seeds for randomized data.

What to install (modules & tools)

  • firebase-tools (CLI, global or dev dep): deploy, emulators, login
  • firebase-functions (v2 API): onRequest, onSchedule, onMessagePublished, onObjectFinalized…
  • firebase-admin: server-side access to Firestore/Auth/Storage
  • typescript, @types/node, eslint: DX & correctness
  • vitest or jest (optional): tests
  • gcloud (optional): managing secrets, infra from CLI
# minimal set inside functions/ npm i firebase-admin firebase-functions npm i -D typescript @types/node eslint # helpful extras npm i -D vitest tsx

Checklist before production

  • ✅ Explicit region, timeoutSeconds, memory, cpu, concurrency, maxInstances
  • ✅ Secrets in Secret Manager via defineSecret; never hard-coded
  • ✅ HTTP endpoints stateless; heavy work async via Pub/Sub
  • ✅ Emulators in CI; unit + integration tests green
  • ✅ Logs & metrics dashboards (latency, errors, cold starts, instance count)
  • ✅ Billing alerts + budgets configured

Bottom line: Gen 2 gives you the knobs to build reliable, cost-controlled serverless backends. Be intentional about concurrency and instances, keep secrets out of code, and use the Emulator Suite to catch issues early.

Work with Sparkle Intelligence

We build intelligent web, mobile, and AI products for modern businesses.

Start a conversation

Leave a Reply

Your email address will not be published. Required fields are marked *