はじめに
「Cloud Runは大げさ、でもサーバーレスで HTTP エンドポイントが欲しい」——そんなときに最適なのが GCP Cloud Functions Gen2 です。
この記事では Claude Code を活用して、4 種類のトリガー(HTTP・Pub/Sub・Firestore・Cloud Scheduler)を持つ Cloud Functions を爆速で実装する方法を解説します。コードはすべてコピペで動くレベルで掲載します。
なぜ Cloud Functions に Claude Code が向いているか
Cloud Functions は定型パターンが多く、Claude Code との相性が抜群です。
- トリガー定義コードが毎回ほぼ同じ → パターン補完が効く
- gcloud デプロイコマンドのオプションが多い → 抜け漏れを防げる
- Functions Framework のローカル設定が初見で詰まりやすい → 一括生成できる
1. HTTP トリガー関数
最もよく使う構成です。Bearer トークン検証 + Firestore ログ書き込みのパターン。
import { http, HttpFunction } from "@google-cloud/functions-framework";
import { Request, Response } from "express";
import { Firestore } from "@google-cloud/firestore";
const db = new Firestore();
const VALID_TOKEN = process.env.API_SECRET_TOKEN;
interface ActionRequest {
userId: string;
action: string;
}
const handleAction: HttpFunction = async (req: Request, res: Response) => {
// CORS プリフライト対応
res.set("Access-Control-Allow-Origin", "*");
if (req.method === "OPTIONS") {
res.set("Access-Control-Allow-Methods", "POST");
res.set("Access-Control-Allow-Headers", "Authorization, Content-Type");
res.status(204).send("");
return;
}
if (req.method !== "POST") {
res.status(405).json({ success: false, message: "Method Not Allowed" });
return;
}
// Bearer トークン検証
const authHeader = req.headers.authorization ?? "";
if (!authHeader.startsWith("Bearer ") || authHeader.slice(7) !== VALID_TOKEN) {
res.status(401).json({ success: false, message: "Unauthorized" });
return;
}
const body = req.body as Partial<ActionRequest>;
if (!body.userId || !body.action) {
res.status(400).json({ success: false, message: "userId and action are required" });
return;
}
try {
const logRef = await db.collection("action_logs").add({
userId: body.userId,
action: body.action,
timestamp: new Date(),
ip: req.ip,
});
res.status(200).json({ success: true, logId: logRef.id });
} catch (err) {
console.error("Firestore write error:", err);
res.status(500).json({ success: false, message: "Internal Server Error" });
}
};
http("handleAction", handleAction);
2. Pub/Sub トリガー関数
非同期イベント処理の定番パターン。例外をスロー = Pub/Sub が再試行、正常リターン = メッセージ確認済みというセマンティクスが重要です。
import { cloudEvent, CloudEvent } from "@google-cloud/functions-framework";
import { MessagePublishedData } from "@google/events/cloud/pubsub/v1/MessagePublishedData";
import { Storage } from "@google-cloud/storage";
const storage = new Storage();
interface ImageUploadedMessage {
bucketName: string;
filePath: string;
}
cloudEvent<MessagePublishedData>("handleImageUploaded", async (event: CloudEvent<MessagePublishedData>) => {
const base64Data = event.data?.message?.data;
if (!base64Data) return; // データなし → 再試行不要
// base64 デコード
const rawJson = Buffer.from(base64Data, "base64").toString("utf-8");
let payload: ImageUploadedMessage;
try {
payload = JSON.parse(rawJson) as ImageUploadedMessage;
} catch {
return; // JSON パースエラーは再試行しても無駄
}
try {
const [metadata] = await storage
.bucket(payload.bucketName)
.file(payload.filePath)
.getMetadata();
console.log("Processed:", metadata.name, metadata.size);
} catch (err) {
throw err; // ← これで Pub/Sub が再試行する
}
});
3. Firestore トリガー関数
before.exists と after.exists の組み合わせで作成・更新・削除を判別します。
import { onDocumentWritten, Change, FirestoreEvent } from "firebase-functions/v2/firestore";
import { QueryDocumentSnapshot } from "firebase-admin/firestore";
import * as admin from "firebase-admin";
admin.initializeApp();
const db = admin.firestore();
export const onUserWrite = onDocumentWritten(
"users/{userId}",
async (event: FirestoreEvent<Change<QueryDocumentSnapshot> | undefined, { userId: string }>) => {
const { userId } = event.params;
const before = event.data?.before;
const after = event.data?.after;
// 作成
if (!before?.exists && after?.exists) {
await db.collection("email_queue").add({
to: after.data()?.email,
template: "welcome",
createdAt: admin.firestore.FieldValue.serverTimestamp(),
status: "pending",
});
}
// 削除 → アーカイブ
if (before?.exists && !after?.exists) {
await db.collection("deleted_users").doc(userId).set({
...before.data(),
deletedAt: admin.firestore.FieldValue.serverTimestamp(),
});
}
}
);
4. ローカルテスト(Functions Framework)
# セットアップ
npm install --save-dev @google-cloud/functions-framework
npm run build
# 起動
npx @google-cloud/functions-framework --target=handleAction --port=8080
# HTTP テスト
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-token" \
-d '{"userId": "user-123", "action": "login"}'
# Pub/Sub エミュレート
MESSAGE_DATA=$(echo -n '{"bucketName":"my-bucket","filePath":"test.png"}' | base64)
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d "{\"specversion\":\"1.0\",\"type\":\"google.cloud.pubsub.topic.v1.messagePublished\",\"source\":\"//pubsub.googleapis.com\",\"id\":\"test-id\",\"data\":{\"message\":{\"data\":\"$MESSAGE_DATA\",\"attributes\":{}}}}"
5. デプロイコマンド
gcloud functions deploy handleAction \
--gen2 \
--runtime=nodejs22 \
--region=asia-northeast1 \
--source=. \
--entry-point=handleAction \
--trigger-http \
--allow-unauthenticated \
--memory=512Mi \
--timeout=60s \
--min-instances=0 \
--max-instances=100 \
--set-secrets="API_SECRET_TOKEN=api-secret-token:latest" \
--set-env-vars="NODE_ENV=production"
落とし穴 4 選
| 落とし穴 | 原因 | 対策 |
|---|---|---|
| コールドスタート遅延 | インスタンス起動に 2〜5 秒 |
--min-instances=1 を設定 |
| タイムアウト 9 分制限 | Gen2 の上限は 540 秒 | Pub/Sub でチャンク分割処理 |
| Secret Manager 権限エラー | 実行 SA に権限がない |
secretmanager.secretAccessor ロールを付与 |
| メモリ不足 OOM | デフォルト 256MB は少ない |
--memory=2Gi --cpu=2 に変更 |
Secret Manager の権限付与コマンド:
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
まとめ
| トリガー | 用途 | Claude Code で生成できるもの |
|---|---|---|
| HTTP | Webhook・API | 認証・バリデーション・エラーハンドリング一式 |
| Pub/Sub | 非同期処理 | base64 デコード・再試行ロジック |
| Firestore | データ変更反応 | 作成/更新/削除の判別ロジック |
| Cloud Scheduler | 定時バッチ | OIDC 検証・タイムゾーン計算 |
Claude Code に「GCP Cloud Functions の HTTP トリガーを TypeScript で書いて、Bearer トークン検証付き」と依頼するだけで、本記事のようなコードが数秒で出てきます。あとはプロジェクトの要件に合わせて微調整するだけです。
より詳しく読みたい方へ
実務レベルの詳細(Cloud Scheduler + OIDC 連携・GitHub Actions CI/CD・コールドスタート計測結果)は以下のブログ記事で解説しています。
Claude Code × GCP Cloud Functions 完全ガイド | claudecode-lab.com