Use Cases (Aktualisiert: 3.6.2026)

GCP Cloud Functions mit Claude Code: HTTP, Secret Manager, Cloud Logging

Cloud Run functions mit Claude Code bauen: HTTP, Secrets, Logging, Deploy-Checks und typische Fehler.

GCP Cloud Functions mit Claude Code: HTTP, Secret Manager, Cloud Logging

Google beschreibt Cloud Functions in der aktuellen Dokumentation häufig über den Pfad Cloud Run functions. Praktisch heißt das: Du stellst Quellcode bereit, Google Cloud baut daraus ein Container-Image, und die Funktion läuft als Cloud Run Service, der per HTTP oder Event ausgelöst wird.

Dieser Leitfaden zeigt, wie ich GCP Cloud Functions mit Claude Code aufsetze: eine Node.js-HTTP-Funktion, eine Cloud Storage Event-Funktion über Eventarc, Secret Manager, Cloud Logging und die Prüfung nach dem Deployment. Eventarc ist die Ereigniszustellung in Google Cloud. Functions Framework ist die Laufzeit-Schicht, mit der dieselbe Funktionsform lokal und in der Cloud funktioniert.

Bei ClaudeCodeLab nutze ich Cloud Run functions für kleine, klar abgegrenzte Aufgaben: Webhook-Empfang, leichte Benachrichtigungen, CSV-Importe oder den Startpunkt eines geplanten Jobs. Cloud Run ist besser, wenn mehrere Routen, eine vollständige API, Next.js oder Express, WebSockets, ein eigenes Dockerfile, lange Laufzeiten oder feine Containerkontrolle nötig sind. Dazu passt der interne GCP Cloud Run Guide.

Claude Code kann die wiederholbaren Teile gut erzeugen: package.json, Functions-Framework-Registrierung, curl-Befehle, gcloud run deploy, IAM-Notizen und Review-Prompts. Menschliche Kontrolle bleibt bei Authentifizierung, Secret Manager, Wiederholungen, Idempotenz und Logs entscheidend. Idempotenz bedeutet, dass dieselbe Anfrage oder dasselbe Event zweimal ankommen kann, ohne doppelte Nebenwirkungen zu erzeugen.


Geeignete Anwendungsfälle

AnwendungsfallEinstiegWorauf im Review zu achten ist
Stripe-, GitHub- oder interne WebhooksHTTP-FunktionSignaturprüfung, Bearer-Token, Replay-Schutz, Secret Manager
CSV-Import nach Cloud-Storage-UploadEventarc + CloudEvent-FunktionDoppelte Events, Bucket-Region, Dateinamenregeln
Kontaktformular-BenachrichtigungHTTP-Funktion oder Pub/Sub-ÜbergabeSchnelle 200-Antwort, Queue-Übergabe, Rate Limits
Nächtlicher Sync oder Report-StartCloud Scheduler + HTTP-FunktionOIDC-Authentifizierung, Zeitzone, Timeout

Diese Fälle passen gut zu Claude Code, weil Eingabe, Validierung, Logging und Fehlerverhalten leicht als Checkliste formulierbar sind. Instabil wird es, wenn eine Funktion viele fachliche Aufgaben bekommt, lange Schleifen ausführt oder als komplette öffentliche API dienen soll.

Für Teamprozesse passen Claude Code Code Review und Secret Management mit Claude Code als weiterführende interne Links.


Minimales Projekt

Das Beispiel nutzt CommonJS-Node.js, damit kein Build-Schritt nötig ist. Die offiziellen Google-Cloud-Beispiele registrieren Funktionen mit dem Functions Framework, das auch lokal läuft.

functions-demo/
  index.js
  package.json
{
  "name": "claude-code-gcp-functions-demo",
  "version": "1.0.0",
  "private": true,
  "main": "index.js",
  "scripts": {
    "start:http": "functions-framework --target=handleAction --port=8080",
    "start:event": "functions-framework --target=handleStorageObject --signature-type=cloudevent --port=8081"
  },
  "dependencies": {
    "@google-cloud/firestore": "^7.11.0",
    "@google-cloud/functions-framework": "^3.4.6"
  }
}
npm install

Funktionscode

Speichere diesen Inhalt als index.js. Die HTTP-Funktion prüft Authorization: Bearer ... und nutzt Idempotency-Key gegen doppelte Verarbeitung. Die Storage-Funktion speichert die CloudEvent-ID in Firestore, damit ein erneut zugestelltes Event nicht denselben Job zweimal erzeugt.

const functions = require("@google-cloud/functions-framework");
const { Firestore } = require("@google-cloud/firestore");
const crypto = require("node:crypto");

const db = new Firestore();

function jsonLog(severity, message, extra = {}) {
  console.log(JSON.stringify({ severity, message, ...extra }));
}

function requireBearerToken(req) {
  const expected = process.env.API_TOKEN;
  const header = req.get("Authorization") || "";
  return Boolean(expected && header === `Bearer ${expected}`);
}

function stableHash(value) {
  return crypto.createHash("sha256").update(value).digest("hex");
}

functions.http("handleAction", async (req, res) => {
  if (req.method !== "POST") {
    res.status(405).json({ ok: false, error: "POST only" });
    return;
  }

  if (!requireBearerToken(req)) {
    res.status(401).json({ ok: false, error: "invalid token" });
    return;
  }

  const body = req.body || {};
  if (typeof body.userId !== "string" || typeof body.action !== "string") {
    res.status(400).json({ ok: false, error: "userId and action are required" });
    return;
  }

  const idempotencyKey =
    req.get("Idempotency-Key") ||
    stableHash(`${body.userId}:${body.action}:${body.requestedAt || ""}`);

  const requestRef = db.collection("function_requests").doc(idempotencyKey);
  const logRef = db.collection("action_logs").doc(idempotencyKey);

  try {
    let duplicate = false;
    await db.runTransaction(async (tx) => {
      const existing = await tx.get(requestRef);
      if (existing.exists) {
        duplicate = true;
        return;
      }

      tx.create(requestRef, {
        userId: body.userId,
        action: body.action,
        createdAt: new Date(),
        source: "handleAction"
      });
      tx.set(logRef, {
        userId: body.userId,
        action: body.action,
        createdAt: new Date()
      });
    });

    jsonLog("INFO", "action accepted", { userId: body.userId, duplicate });
    res.status(200).json({ ok: true, duplicate, idempotencyKey });
  } catch (error) {
    jsonLog("ERROR", "action failed", { error: String(error) });
    res.status(500).json({ ok: false, error: "internal error" });
  }
});

functions.cloudEvent("handleStorageObject", async (cloudEvent) => {
  const data = cloudEvent.data || {};
  const bucket = data.bucket;
  const name = data.name;

  if (!bucket || !name) {
    jsonLog("WARNING", "storage event missing bucket or name", { eventId: cloudEvent.id });
    return;
  }

  const eventRef = db.collection("processed_storage_events").doc(cloudEvent.id);
  const jobRef = db.collection("storage_import_jobs").doc(stableHash(`${bucket}/${name}`));

  await db.runTransaction(async (tx) => {
    const existing = await tx.get(eventRef);
    if (existing.exists) {
      jsonLog("INFO", "duplicate storage event ignored", { eventId: cloudEvent.id });
      return;
    }

    tx.create(eventRef, {
      bucket,
      name,
      eventType: cloudEvent.type,
      createdAt: new Date()
    });
    tx.set(jobRef, {
      bucket,
      name,
      status: "queued",
      updatedAt: new Date()
    }, { merge: true });
  });

  jsonLog("INFO", "storage import job queued", { bucket, name, eventId: cloudEvent.id });
});

Strukturierte Logs machen Cloud Logging im Störfall nutzbar. Cloud Run leitet stdout und stderr automatisch an Cloud Logging weiter. JSON-Zeilen mit severity, message, eventId und userId sind deutlich besser filterbar als freie Textmeldungen.


Lokal testen

HTTP-Funktion starten:

export API_TOKEN="local-token"
npm run start:http

Anfrage aus einem zweiten Terminal senden:

curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer local-token" \
  -H "Idempotency-Key: local-001" \
  -d '{"userId":"user-123","action":"login","requestedAt":"2026-06-03T00:00:00Z"}'

CloudEvent-Funktion starten:

npm run start:event
curl -X POST http://localhost:8081 \
  -H "Content-Type: application/json" \
  -H "ce-id: local-event-001" \
  -H "ce-specversion: 1.0" \
  -H "ce-type: google.cloud.storage.object.v1.finalized" \
  -H "ce-source: //storage.googleapis.com/projects/_/buckets/demo-bucket" \
  -d '{"bucket":"demo-bucket","name":"inbox/sample.csv","metageneration":"1"}'

Wenn der lokale Code Firestore nutzt, brauchst du gcloud auth application-default login oder ein separates Testprojekt. Smoke-Tests sollten nicht auf Produktionsdaten zeigen.


Secrets und IAM

Token gehören nicht in Quellcode oder .env-Dateien, die im Review landen. Lege sie im Secret Manager ab und gib nur dem Runtime-Servicekonto Zugriff.

PROJECT_ID="$(gcloud config get-value project)"
REGION="asia-northeast1"
RUNTIME_SA="functions-runtime@${PROJECT_ID}.iam.gserviceaccount.com"

gcloud iam service-accounts create functions-runtime \
  --display-name="Functions runtime service account"

printf "replace-with-real-token" | gcloud secrets create api-token \
  --replication-policy="automatic" \
  --data-file=-

gcloud secrets add-iam-policy-binding api-token \
  --member="serviceAccount:${RUNTIME_SA}" \
  --role="roles/secretmanager.secretAccessor"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
  --member="serviceAccount:${RUNTIME_SA}" \
  --role="roles/datastore.user"

Trenne zwei Identitäten sauber: Die deployende Person braucht Rechte zum Erstellen der Cloud Run function und zum Verwenden des Servicekontos. Das Runtime-Servicekonto bekommt nur die Rechte, die der Code wirklich benötigt.


Deployment mit gcloud run

Die aktuelle Dokumentation zeigt den Weg über gcloud run deploy. Für einen privaten HTTP-Endpunkt beginnt man authentifiziert:

gcloud run deploy handle-action \
  --source . \
  --function handleAction \
  --base-image nodejs24 \
  --region "${REGION}" \
  --service-account "${RUNTIME_SA}" \
  --no-allow-unauthenticated \
  --memory 512Mi \
  --timeout 60s \
  --max-instances 20

gcloud run services update handle-action \
  --region "${REGION}" \
  --update-secrets=API_TOKEN=api-token:latest

Für Storage-Events zuerst den Service deployen und dann den Eventarc-Trigger erstellen:

BUCKET="your-import-bucket"
EVENTARC_SA="eventarc-invoker@${PROJECT_ID}.iam.gserviceaccount.com"

gcloud iam service-accounts create eventarc-invoker \
  --display-name="Eventarc trigger invoker"

gcloud run deploy storage-import \
  --source . \
  --function handleStorageObject \
  --base-image nodejs24 \
  --region "${REGION}" \
  --service-account "${RUNTIME_SA}" \
  --no-allow-unauthenticated \
  --memory 512Mi \
  --timeout 120s \
  --max-instances 10

gcloud run services add-iam-policy-binding storage-import \
  --region "${REGION}" \
  --member="serviceAccount:${EVENTARC_SA}" \
  --role="roles/run.invoker"

gcloud eventarc triggers create storage-finalized-to-function \
  --location="${REGION}" \
  --destination-run-service=storage-import \
  --destination-run-region="${REGION}" \
  --event-filters="type=google.cloud.storage.object.v1.finalized" \
  --event-filters="bucket=${BUCKET}" \
  --service-account="${EVENTARC_SA}"

Eventarc-Trigger brauchen manchmal einige Minuten, bis sie aktiv sind. Plane Bucket-Region, Trigger-Location und Cloud-Run-Region gemeinsam, weil sie Latenz, Datenhaltung und Kosten beeinflussen.


Logs und Prüfung

Nach dem Deployment reichen URL und grüner Status nicht. Teste den Endpunkt und prüfe Logs:

SERVICE_URL="$(gcloud run services describe handle-action \
  --region "${REGION}" \
  --format='value(status.url)')"

curl -X POST "${SERVICE_URL}" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer replace-with-real-token" \
  -H "Idempotency-Key: prod-smoke-001" \
  -d '{"userId":"smoke-user","action":"deploy-check","requestedAt":"2026-06-03T00:00:00Z"}'

gcloud run services logs read handle-action \
  --region "${REGION}" \
  --limit 20

gcloud logging read \
  'resource.type="cloud_run_revision" AND resource.labels.service_name="handle-action" AND jsonPayload.message="action accepted"' \
  --limit 20 \
  --format json

Logge genug für die Fehlersuche: Event-ID, Dateiname, Duplicate-Flag, externe API-Statuscodes und sichere fachliche IDs. Token, vollständige Nachrichtenkörper und personenbezogene Daten gehören nicht ins Log.


Typische Fehler

Erstens erzeugen Retries ohne Idempotenz doppelte Nebenwirkungen. Rechnungen, E-Mails, Lagerbestand und Imports brauchen eine gespeicherte Event-ID oder einen Geschäftsschlüssel.

Zweitens muss Secret-Manager-Zugriff beim Runtime-Servicekonto liegen. Ein Deployment kann erfolgreich sein, während produktive Requests mit Permission denied scheitern.

Drittens brauchen öffentliche HTTP-Funktionen mehr Schutz als --allow-unauthenticated: Signaturen, Rate Limits und bei Bedarf API Gateway oder Cloud Armor. Interne Jobs sollten authentifiziert aufrufen.

Viertens ist eine Cloud Run function kein Ersatz für eine komplette Anwendung. Viele Routen, lange Jobs, eigene Systempakete, GPU oder feine Containersteuerung gehören eher zu Cloud Run, Cloud Run jobs oder Workflows.

Fünftens sollten Kosten und Cleanup Teil des Prompts sein. Auch bei Scale-to-zero können Artifact Registry, Cloud Build, Storage, Eventarc und Cloud Logging Kosten erzeugen.


Review-Prompt

Review this Cloud Run functions implementation.
Check:
- Functions Framework registration, gcloud run deploy --function, and package.json target match
- HTTP authentication, input validation, and error responses are safe
- Eventarc retries cannot create duplicate side effects
- Secret Manager values are not logged or returned in exceptions
- The runtime service account has only the minimum IAM roles
- Cloud Logging entries are structured enough for incident review
- Any workload that should be Cloud Run is not forced into a function

If there are issues, return severity, reason, corrected code, and verification commands.

ClaudeCodeLab überführt solche Betriebs-Checklisten in Produkte und Templates sowie Team-Trainings. Das ist hilfreich, wenn Serverless-Reviews nicht vom Gedächtnis einer einzelnen Senior-Person abhängen sollen.


Offizielle Dokumentation


Ergebnis

Die getestete Form ist klein, aber nah an Produktion: Node.js 24 Cloud Run functions, lokale Functions-Framework-Kommandos, HTTP- und CloudEvent-Checks mit curl, Secret-Manager-Injektion, Firestore-Idempotenz und Cloud-Logging-Abfragen. Entscheidend ist, Identität, Logs, Retries, Region und Kosten zu prüfen, bevor echter Traffic auf die Funktion geht.

#claude-code #gcp #cloud-functions #typescript #serverless #pubsub
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.