Claude Code × GCP Cloud Functions Guía Completa | Desarrollo Serverless Ultrarrápido
Optimiza GCP Cloud Functions con Claude Code. Implementa triggers HTTP/Pub/Sub/Firestore, pruebas locales y automatización de despliegues con ejemplos de código reales de la experiencia de Masa.
Cloud Run era excesivo — por eso elegí Cloud Functions
Soy Masa, desarrollador de claudecode-lab.com.
Cuando empecé a usar GCP Serverless, consideré Cloud Run. Flexible y basado en contenedores, pero para tareas como “solo recibir un webhook” o “ejecutar un batch una vez por la noche”, era claramente sobredimensionado. Ahí fue cuando cambié a Cloud Functions Gen2.
Con Claude Code, más del 90% de mi código de Cloud Functions está generado automáticamente. Me concentro en la revisión y validación.
Paso 1: Función con 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) => {
// Configurar headers 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;
}
// Solo permitir POST
if (req.method !== "POST") {
res.status(405).json({ success: false, message: "Method Not Allowed" });
return;
}
// Validar Bearer token
const authHeader = req.headers.authorization ?? "";
if (!authHeader.startsWith("Bearer ") || authHeader.slice(7) !== VALID_TOKEN) {
res.status(401).json({ success: false, message: "No autorizado" });
return;
}
const body = req.body as Partial<ActionRequest>;
if (!body.userId || !body.action) {
res.status(400).json({ success: false, message: "userId y action son requeridos" });
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: "Acción registrada", logId: logRef.id });
} catch (err) {
console.error("Error al escribir en Firestore:", err);
res.status(500).json({ success: false, message: "Error interno del servidor" });
}
};
http("handleAction", handleAction);
Paso 2: Función con 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("No se recibieron datos del mensaje, omitiendo");
return;
}
const rawJson = Buffer.from(base64Data, "base64").toString("utf-8");
let payload: ImageUploadedMessage;
try {
payload = JSON.parse(rawJson) as ImageUploadedMessage;
} catch {
console.error("JSON inválido en mensaje Pub/Sub:", rawJson);
return;
}
const { bucketName, filePath } = payload;
try {
const [metadata] = await storage.bucket(bucketName).file(filePath).getMetadata();
console.log("Metadatos del archivo:", { nombre: metadata.name, tamaño: metadata.size });
} catch (err) {
// Lanzar excepción hace que Pub/Sub reintente el mensaje
console.error(`Error al procesar ${filePath}:`, err);
throw err;
}
});
Paso 3: Función con 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;
// Evento de creación
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: "pendiente",
});
return;
}
// Evento de eliminación — copiar al archivo
if (before?.exists && !after?.exists) {
await db.collection("deleted_users").doc(userId).set({
...before.data(),
deletedAt: admin.firestore.FieldValue.serverTimestamp(),
});
}
}
);
Paso 4: Pruebas Locales y Despliegue
# Inicio local
npm run build
npx @google-cloud/functions-framework --target=handleAction --port=8080
# Enviar solicitud de prueba
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer token-de-prueba" \
-d '{"userId": "user-123", "action": "login"}'
# Despliegue en GCP
gcloud functions deploy handleAction \
--gen2 \
--runtime=nodejs22 \
--region=us-central1 \
--source=. \
--entry-point=handleAction \
--trigger-http \
--allow-unauthenticated \
--memory=512Mi \
--timeout=60s \
--set-secrets="API_SECRET_TOKEN=api-secret-token:latest"
4 Errores Comunes
| Error | Problema | Solución |
|---|---|---|
| Cold Start | Latencia de 2–5 s con instancias inactivas | --min-instances=1 para funciones críticas |
| Límite de timeout | Máximo 9 minutos (540 s) | Dividir tareas grandes con Pub/Sub |
| Permisos Secret Manager | Permission denied en tiempo de ejecución | Otorgar rol secretmanager.secretAccessor |
| Falta de memoria | OOM con procesamiento pesado | Configurar --memory=2Gi --cpu=2 |
Resumen
| Trigger | Caso de Uso | Consideración Principal |
|---|---|---|
| HTTP | Webhooks, endpoints API | Autenticación, CORS |
| Pub/Sub | Procesamiento asíncrono de eventos | Decodificación base64, diseño de reintentos |
| Firestore | Reaccionar a cambios de datos | Evitar bucles infinitos |
| Cloud Scheduler | Batch jobs programados | Validación OIDC, zona horaria |
Artículos Relacionados
PDF gratuito: Hoja de trucos de Claude Code en 5 minutos
Solo deja tu correo y te enviaremos al instante la hoja de trucos en una página A4.
Cuidamos tus datos personales y nunca enviamos spam.
Lleva tu flujo con Claude Code al siguiente nivel
50 plantillas de prompts probadas en producción, listas para copiar y pegar en Claude Code.
Sobre el autor
Masa
Ingeniero apasionado por Claude Code. Dirige claudecode-lab.com, un medio tecnológico en 10 idiomas con más de 2.000 páginas.
Artículos relacionados
Que es Codex Automations y como dejar que la IA gestione contenido mientras duermes
Guia practica para usar Codex Automations en analitica, articulos, CTA, despliegue y monetizacion.
Diseña Firestore con Claude Code: empieza por consultas, no colecciones
Flujo práctico para diseñar Firestore con Claude Code: esquema guiado por consultas, índices, costos, reglas de seguridad y TypeScript.
Claude Code × GCP Cloud Run Guía Completa | Despliegue automático de contenedores serverless
Acelera los despliegues en GCP Cloud Run con Claude Code. Guía completa con ejemplos de código reales: generación de Dockerfile, auto-escalado, pipelines CI/CD e integración con Secret Manager.