Cloud Functions
Cloud Functions for Firebase run your backend code in response to HTTPS calls, Firestore writes, Auth events, Pub/Sub messages, scheduled cron, and more. The v2 SDK simplifies regions, concurrency, and secrets — it’s the easiest way to add a server-side surface to a Firebase app without managing servers.
HTTPS, triggers, secrets, scheduled
EXAMPLE
// 1) Setup
// firebase init functions
// Choose TypeScript, install deps
// functions/src/index.ts
import { onRequest, onCall } from 'firebase-functions/v2/https';
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
import { onObjectFinalized } from 'firebase-functions/v2/storage';
import { beforeUserCreated } from 'firebase-functions/v2/identity';
import { onMessagePublished } from 'firebase-functions/v2/pubsub';
import { onSchedule } from 'firebase-functions/v2/scheduler';
import { setGlobalOptions } from 'firebase-functions/v2';
import { defineSecret } from 'firebase-functions/params';
import { initializeApp } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore';
import { getAuth } from 'firebase-admin/auth';
setGlobalOptions({ region: 'australia-southeast1', maxInstances: 10 });
initializeApp();
// 2) HTTPS function (REST endpoint)
export const hello = onRequest({ cors: true }, async (req, res) => {
res.json({ msg: `hello, ${req.query.name ?? 'world'}` });
});
// Deploy: firebase deploy --only functions:hello
// URL: https://australia-southeast1-<project>.cloudfunctions.net/hello
// 3) Callable function — client calls with the Firebase SDK; auth + JSON handled for you
import { HttpsError } from 'firebase-functions/v2/https';
export const createPost = onCall(async (req) => {
if (!req.auth) throw new HttpsError('unauthenticated', 'Sign in required');
const { title, body } = req.data ?? {};
if (!title?.length) throw new HttpsError('invalid-argument', 'title required');
const docRef = await getFirestore().collection('posts').add({
title,
body,
authorId: req.auth.uid,
createdAt: new Date(),
});
return { id: docRef.id };
});
// Client (web)
import { getFunctions, httpsCallable } from 'firebase/functions';
const fns = getFunctions(app, 'australia-southeast1');
const create = httpsCallable(fns, 'createPost');
await create({ title: 'Hi', body: 'World' });
// 4) Firestore trigger — react to data changes
export const onUserCreated = onDocumentCreated('users/{uid}', async (event) => {
const user = event.data?.data();
if (!user) return;
await getFirestore().collection('audit').add({
action: 'user.created',
uid: event.params.uid,
at: new Date(),
});
});
// 5) Auth trigger
export const blockBadDomains = beforeUserCreated((event) => {
if (event.data.email?.endsWith('@blocked.example')) {
throw new HttpsError('permission-denied', 'Domain not allowed');
}
});
// 6) Storage trigger — resize uploaded images
export const onImageUploaded = onObjectFinalized({ region: 'australia-southeast1' }, async (event) => {
const { bucket, name, contentType } = event.data;
if (!contentType?.startsWith('image/')) return;
if (name?.includes('thumb_')) return; // avoid recursion
// resize logic …
});
// 7) Pub/Sub trigger
export const handleSignupEvent = onMessagePublished({ topic: 'signups' }, async (event) => {
const data = event.data.message.json;
// process the message
});
// 8) Scheduled function
export const dailySummary = onSchedule(
{ schedule: 'every day 06:00', timeZone: 'Australia/Sydney' },
async () => {
const db = getFirestore();
// generate yesterday's report and email it
},
);
// 9) Secrets
const SENDGRID_KEY = defineSecret('SENDGRID_API_KEY');
export const sendNewsletter = onSchedule(
{ schedule: 'every monday 08:00', secrets: [SENDGRID_KEY] },
async () => {
const key = SENDGRID_KEY.value();
// call sendgrid with `key`
},
);
// Set: firebase functions:secrets:set SENDGRID_API_KEY
// 10) Configuration options
export const heavy = onRequest(
{
region: 'us-central1',
memory: '1GiB', // 256MiB / 512MiB / 1GiB / 2GiB / 4GiB / 8GiB
cpu: 1,
timeoutSeconds: 540, // max 60 min for v2
concurrency: 80,
maxInstances: 100,
minInstances: 1, // keep warm to avoid cold starts ($$)
ingressSettings: 'ALLOW_INTERNAL_ONLY',
},
async (req, res) => { /* … */ },
);
// 11) CORS + auth on HTTPS
import cors from 'cors';
const corsHandler = cors({ origin: ['https://app.example.com'], credentials: true });
export const api = onRequest({ cors: false }, (req, res) => {
corsHandler(req, res, async () => {
// verify ID token
const idToken = req.headers.authorization?.replace('Bearer ', '');
if (!idToken) return res.sendStatus(401);
try {
const decoded = await getAuth().verifyIdToken(idToken);
res.json({ uid: decoded.uid });
} catch (e) {
res.sendStatus(401);
}
});
});
// 12) Emulator suite for local dev
// firebase emulators:start --only functions,firestore
// Functions hot-reload; triggers fire from emulated Firestore/Auth/Storage.
// Same code runs in prod with no changes.
// 13) Deployment
// firebase deploy --only functions # all functions
// firebase deploy --only functions:hello,functions:api # selective
// firebase functions:delete oldFunc # remove a deployed function
// 14) Logs + monitoring
// firebase functions:log --tail
// Cloud Logging in GCP console for filtering and metrics.
// Set up uptime checks for HTTPS endpoints; alerts on error rate.
// 15) Cost tips
// • minInstances costs money (always-running) — use only for latency-sensitive endpoints
// • concurrency > 1 (v2 default 80) shares one instance among many requests → cheaper
// • Reduce cold starts: keep deps slim, use top-level await sparingly, prefer ESM
// • Choose region close to users + Firestore data → less egress
// • Watch egress to internet vs Google services
// 16) Common bugs
// • Forgot to call initializeApp() → admin SDK throws
// • Cold start hits user on first request → minInstances or warm-up requests
// • Region mismatch between client + function → cross-region call adds latency
// • Function throws but client gets timeout → throw HttpsError with code for callables
// • Storage trigger recursion (resize → upload → re-trigger) → guard with name prefix/path
// • Background trigger lacks idempotency → retries duplicate side effects; use idempotency keys
// • Secret used before defining → undefined; ensure secret in 'secrets' array on function options
// • Heavy npm dep bundle → slow cold starts; trim deps + use esbuild bundling for v2
Why it matters
Cloud Functions v2 covers HTTPS, callable, Firestore, Auth, Storage, Pub/Sub, and cron triggers from one SDK. Pick the right region close to your users and data, lean on onCall for client-callable endpoints (it handles auth + CORS), keep secrets in defineSecret instead of env files, and guard background triggers against recursion + non-idempotent retries.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// functions/src/index.ts
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
export const onPostCreate = onDocumentCreated('posts/{id}', (event) => {
const post = event.data?.data();
console.log('new post', post?.title);
});
Try it Yourself »
Exercise
Trigger on doc creation.
export const fn =
('posts/{id}', ev => { });
PascalCase, starts with onDocument.
Discussion
Loading…