All topics
Cloud · Learning hub

Cloud Functions notes for developers

Master Cloud Functions with a curated set of 1 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallTest yourself — Cloud Functions quizMore Cloud notes
Cloud Functions

Cloud Functions Essentials

Cloud Functions Essentials What It Is: Event-Driven vs HTTP Google Cloud Functions is GCP's Function-as-a-Service offering — a single function deployed and invo

Cloud Functions Essentials

What It Is: Event-Driven vs HTTP

Google Cloud Functions is GCP's Function-as-a-Service offering — a single function deployed and invoked without managing servers, GCP's answer to AWS Lambda and Azure Functions. Unlike Azure Functions' triggers-and-bindings model (where inputs/outputs are declared in config and injected), GCP Cloud Functions takes a simpler two-shape model: an HTTP function receives a plain request/response object, and an event-driven (CloudEvent) function receives a standardized event payload from an Eventarc-routed source (Pub/Sub, Cloud Storage, Firestore, etc.) — there's no binding-injection layer, you read the event object directly in your handler.

  • HTTP function — triggered by a direct HTTPS request; you get a raw request/response, exactly like an Express handler

  • CloudEvent function — triggered by an event (Pub/Sub message, Cloud Storage object change, Firestore write) routed through Eventarc, using the CNCF CloudEvents spec

  • Gen 2 (built on Cloud Run) — the current generation; functions run as Cloud Run services under the hood, giving longer timeouts (up to 60 min), larger instances, and concurrency (multiple requests per instance)

Writing Functions (Node.js)

const functions = require('@google-cloud/functions-framework')

// HTTP function — plain (req, res), no binding config needed
functions.http('createOrder', async (req, res) => {
  if (req.method !== 'POST') {
    return res.status(405).send('Method Not Allowed')
  }

  const { items, customerId } = req.body
  if (!items || items.length === 0) {
    return res.status(400).json({ error: 'items is required' })
  }

  const order = await createOrder({ items, customerId })
  res.status(201).json(order)
})

// CloudEvent function — triggered by a Pub/Sub message via Eventarc
functions.cloudEvent('processOrderEvent', async (cloudEvent) => {
  const base64Data = cloudEvent.data.message.data
  const payload = JSON.parse(Buffer.from(base64Data, 'base64').toString())

  console.log(`Processing order ${payload.orderId}`)
  await fulfillOrder(payload.orderId)
})

// CloudEvent function — triggered by a new object landing in Cloud Storage
functions.cloudEvent('resizeUploadedImage', async (cloudEvent) => {
  const file = cloudEvent.data
  if (!file.contentType?.startsWith('image/')) return

  console.log(`Resizing ${file.name} in bucket ${file.bucket}`)
  await generateThumbnail(file.bucket, file.name)
})

Deployment Tooling

Deployment is a single gcloud command per function — there's no equivalent of Azure's Function App grouping multiple functions under one deployable unit; each Cloud Function deploys and scales independently. Local testing uses the Functions Framework directly rather than a separate CLI tool (Azure's func start).

# Deploy an HTTP function (Gen 2)
gcloud functions deploy createOrder \
  --gen2 \
  --runtime=nodejs20 \
  --region=us-central1 \
  --source=. \
  --entry-point=createOrder \
  --trigger-http \
  --allow-unauthenticated \
  --memory=256Mi \
  --timeout=60s \
  --set-env-vars=DATABASE_URL=postgres://...

# Deploy an event-driven function, triggered by a Pub/Sub topic
gcloud functions deploy processOrderEvent \
  --gen2 \
  --runtime=nodejs20 \
  --region=us-central1 \
  --source=. \
  --entry-point=processOrderEvent \
  --trigger-topic=order-processing

# Deploy a function triggered by Cloud Storage object finalization
gcloud functions deploy resizeUploadedImage \
  --gen2 \
  --runtime=nodejs20 \
  --region=us-central1 \
  --source=. \
  --entry-point=resizeUploadedImage \
  --trigger-bucket=my-uploads-bucket

# Local dev with the Functions Framework — no separate CLI install needed
npx @google-cloud/functions-framework --target=createOrder

# Stream logs
gcloud functions logs read createOrder --region=us-central1 --gen2

Scaling & Pricing Model

Cloud Functions bills per 100ms of compute time plus a per-invocation fee, with a perpetual free tier (2 million invocations/month). Because Gen 2 runs on Cloud Run, you also get concurrency — a single instance can handle multiple in-flight requests if your code is async-safe, which lowers cost for I/O-bound workloads compared to the one-request-per-instance model. This differs from Azure Functions' Consumption plan, which is billed on GB-seconds and executions but doesn't offer per-instance concurrency in the same way.

# Configure min/max instances and concurrency (Gen 2)
gcloud functions deploy createOrder \
  --gen2 \
  --region=us-central1 \
  --min-instances=0 \
  --max-instances=20 \
  --concurrency=40 \
  --cpu=1 \
  --memory=512Mi

# min-instances=0 -> scale to zero, cold starts possible
# min-instances=1 -> always one warm instance, eliminates cold starts, costs more

Pitfalls & Practical Tips

  • Gen 1 and Gen 2 have different limits and behavior (Gen 1 max timeout is 9 minutes, no concurrency; Gen 2 supports up to 60 minutes and per-instance concurrency) — new functions should default to Gen 2 unless you have a specific Gen 1-only dependency

  • Pub/Sub and Storage triggers deliver at-least-once — a message or event can be redelivered after a transient failure, so handlers must be idempotent (check for already-processed IDs before acting)

  • --allow-unauthenticated is required to make an HTTP function publicly callable; without it, callers need a valid Google-signed identity token, which is easy to forget when a frontend suddenly gets 403s

  • Cold starts scale with package size and heavy top-level imports — keep dependencies lean and do expensive client initialization (DB pools, SDK clients) outside the handler so it's reused across warm invocations, not per-request

  • Unlike Azure's cron.yaml, Cloud Functions has no built-in scheduler — pair an HTTP function with Cloud Scheduler (a separate resource that calls the function's URL on a cron schedule)

  • Concurrency > 1 assumes your code has no unsafe shared mutable state between requests on the same instance — CPU-bound or blocking code should keep concurrency at 1 to avoid one request starving another

Keep your Cloud Functions knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever