Use Cases

Claude Code × GCP Cloud Functions Guide Complet | Développement Serverless Ultra-Rapide

Optimisez GCP Cloud Functions avec Claude Code. Implémentez des triggers HTTP/Pub/Sub/Firestore, des tests locaux et l'automatisation des déploiements avec des exemples de code réels de l'expérience de Masa.

Cloud Run était excessif — c’est pourquoi j’ai choisi Cloud Functions

Je suis Masa, développeur de claudecode-lab.com.

Quand j’ai commencé à utiliser GCP Serverless, j’ai envisagé Cloud Run. Flexible et basé sur des conteneurs — mais pour des tâches comme “simplement recevoir un webhook” ou “exécuter un batch une fois la nuit”, c’était clairement surdimensionné. C’est alors que j’ai adopté Cloud Functions Gen2.

Avec Claude Code, plus de 90 % de mon code Cloud Functions est généré automatiquement.


Étape 1 : Fonction avec Trigger HTTP

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) => {
  // Définir les en-têtes 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;
  }

  // Autoriser uniquement POST
  if (req.method !== "POST") {
    res.status(405).json({ success: false, message: "Méthode non autorisée" });
    return;
  }

  // Validation du Bearer token
  const authHeader = req.headers.authorization ?? "";
  if (!authHeader.startsWith("Bearer ") || authHeader.slice(7) !== VALID_TOKEN) {
    res.status(401).json({ success: false, message: "Non autorisé" });
    return;
  }

  const body = req.body as Partial<ActionRequest>;
  if (!body.userId || !body.action) {
    res.status(400).json({ success: false, message: "userId et action sont requis" });
    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, message: "Action enregistrée", logId: logRef.id });
  } catch (err) {
    console.error("Erreur d'écriture Firestore :", err);
    res.status(500).json({ success: false, message: "Erreur interne du serveur" });
  }
};

http("handleAction", handleAction);

Étape 2 : Fonction avec Trigger 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) {
    console.warn("Aucune donnée de message reçue, ignoré");
    return;
  }

  const rawJson = Buffer.from(base64Data, "base64").toString("utf-8");
  let payload: ImageUploadedMessage;
  try {
    payload = JSON.parse(rawJson) as ImageUploadedMessage;
  } catch {
    console.error("JSON invalide dans le message Pub/Sub :", rawJson);
    return;
  }

  const { bucketName, filePath } = payload;
  try {
    const [metadata] = await storage.bucket(bucketName).file(filePath).getMetadata();
    console.log("Métadonnées du fichier :", { nom: metadata.name, taille: metadata.size });
  } catch (err) {
    // Lancer une exception pour que Pub/Sub réessaie le message
    console.error(`Échec du traitement de ${filePath} :`, err);
    throw err;
  }
});

Étape 3 : Fonction avec Trigger Firestore

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.userId;
    const before = event.data?.before;
    const after = event.data?.after;

    // Événement de création
    if (!before?.exists && after?.exists) {
      const userData = after.data();
      await db.collection("email_queue").add({
        to: userData?.email,
        template: "welcome",
        createdAt: admin.firestore.FieldValue.serverTimestamp(),
        status: "en_attente",
      });
      return;
    }

    // Événement de suppression — copier dans l'archive
    if (before?.exists && !after?.exists) {
      await db.collection("deleted_users").doc(userId).set({
        ...before.data(),
        deletedAt: admin.firestore.FieldValue.serverTimestamp(),
      });
    }
  }
);

Étape 4 : Tests Locaux et Déploiement

# Démarrage local
npm run build
npx @google-cloud/functions-framework --target=handleAction --port=8080

# Envoyer une requête de test
curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer token-de-test" \
  -d '{"userId": "user-123", "action": "login"}'

# Déploiement sur GCP
gcloud functions deploy handleAction \
  --gen2 \
  --runtime=nodejs22 \
  --region=europe-west1 \
  --source=. \
  --entry-point=handleAction \
  --trigger-http \
  --allow-unauthenticated \
  --memory=512Mi \
  --timeout=60s \
  --set-secrets="API_SECRET_TOKEN=api-secret-token:latest"

4 Pièges Courants

PiègeProblèmeSolution
Cold StartLatence de 2–5 s avec instances inactives--min-instances=1 pour les fonctions critiques
Limite de timeoutMaximum 9 minutes (540 s)Diviser les tâches longues via Pub/Sub
Permissions Secret ManagerPermission denied à l’exécutionAttribuer le rôle secretmanager.secretAccessor
Manque de mémoireOOM lors de traitements lourdsConfigurer --memory=2Gi --cpu=2

Récapitulatif

TriggerCas d’UsagePoint Clé
HTTPWebhooks, endpoints APIAuthentification, CORS
Pub/SubTraitement asynchrone d’événementsDécodage base64, conception de retry
FirestoreRéagir aux changements de donnéesÉviter les boucles infinies
Cloud SchedulerBatch jobs planifiésValidation OIDC, fuseau horaire

Articles Connexes

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

PDF gratuit : aide-mémoire Claude Code en 5 minutes

Laissez simplement votre e-mail et nous vous enverrons immédiatement l'aide-mémoire A4 en PDF.

Nous traitons vos données avec soin et n'envoyons jamais de spam.

Passez votre flux Claude Code au niveau supérieur

50 modèles de prompts éprouvés, prêts à être copiés-collés dans Claude Code.

Masa

À propos de l'auteur

Masa

Ingénieur passionné par Claude Code. Il gère claudecode-lab.com, un média tech en 10 langues avec plus de 2 000 pages.