GCP Cloud Functions com Claude Code: HTTP, Secret Manager e Cloud Logging
Crie Cloud Run functions com Claude Code: HTTP, segredos, logs, verificação de deploy e armadilhas.
A documentação atual do Google Cloud frequentemente apresenta Cloud Functions pelo caminho de Cloud Run functions. O modelo prático é simples: você envia o código-fonte, o Google Cloud cria um contêiner e o resultado roda como um serviço Cloud Run chamado por HTTP ou por eventos.
Este guia mostra como criar GCP Cloud Functions com Claude Code: uma função HTTP em Node.js, uma função de evento do Cloud Storage via Eventarc, Secret Manager, Cloud Logging e verificação após o deploy. Eventarc é a camada de entrega de eventos do Google Cloud. Functions Framework é o adaptador que permite executar a mesma forma de função localmente e na nuvem.
Na ClaudeCodeLab, uso Cloud Run functions para unidades pequenas e bem delimitadas: receber webhooks, enviar notificações leves, iniciar importações CSV ou servir como entrada de um job agendado. Uso Cloud Run quando preciso de muitas rotas, uma API completa, Next.js ou Express, WebSockets, Dockerfile próprio, processamento longo ou controle detalhado do contêiner. A guia interna de GCP Cloud Run cobre essa decisão.
Claude Code ajuda nas partes repetíveis: package.json, registro no Functions Framework, comandos curl, gcloud run deploy, notas de IAM e prompts de revisão. A revisão humana continua essencial para autenticação, Secret Manager, retries, idempotência e logs. Idempotência significa que receber a mesma requisição ou o mesmo evento duas vezes não corrompe o resultado.
Bons casos de uso
| Caso de uso | Entrada | O que revisar |
|---|---|---|
| Webhooks de Stripe, GitHub ou ferramentas internas | Função HTTP | Assinatura, Bearer token, proteção contra replay, Secret Manager |
| Importação CSV após upload no Cloud Storage | Eventarc + função CloudEvent | Eventos duplicados, região do bucket, regras de nome de arquivo |
| Notificação de formulário de contato | Função HTTP ou Pub/Sub | Resposta 200 rápida, passagem para fila, rate limits |
| Sincronização noturna ou relatório | Cloud Scheduler + função HTTP | Autenticação OIDC, fuso horário, timeout |
Esses casos combinam bem com Claude Code porque entrada, validação, logging e falhas viram uma checklist clara. O desenho fica frágil quando uma função acumula responsabilidades, roda loops longos ou vira uma API pública inteira.
Para processos de equipe, combine com revisão de código com Claude Code e gestão de segredos com Claude Code.
Projeto mínimo
O exemplo usa Node.js CommonJS para não exigir etapa de build. A documentação oficial registra funções com Functions Framework, e o mesmo framework roda localmente.
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
Código da função
Salve este conteúdo como index.js. A função HTTP verifica Authorization: Bearer ... e usa Idempotency-Key para evitar duplicidade. A função de Storage grava o ID do CloudEvent no Firestore para que um evento reenviado não crie o mesmo job duas vezes.
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 });
});
Logs estruturados tornam o Cloud Logging útil em incidentes. Cloud Run envia stdout e stderr automaticamente ao Cloud Logging; linhas JSON com severity, message, eventId e userId são muito mais fáceis de filtrar do que texto solto.
Testes locais
Inicie a função HTTP:
export API_TOKEN="local-token"
npm run start:http
Envie a requisição de outro terminal:
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"}'
Inicie a função CloudEvent:
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"}'
Se o código local conectar ao Firestore, use gcloud auth application-default login ou um projeto de teste dedicado. Não aponte smoke tests para dados de produção.
Secrets e IAM
Não coloque tokens no código nem em arquivos .env que entram em revisão. Guarde-os no Secret Manager e dê acesso apenas à conta de serviço de execução.
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"
Separe duas identidades. Quem faz deploy precisa criar Cloud Run functions e usar a conta de serviço. A conta de execução deve receber somente as permissões mínimas que o código usa.
Deploy com gcloud run
O fluxo atual usa gcloud run deploy. Para um endpoint HTTP privado, comece exigindo autenticação:
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
Para eventos do Storage, faça o deploy do serviço e depois crie o trigger Eventarc:
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}"
Triggers Eventarc podem levar alguns minutos para ficarem ativos. Planeje região do bucket, localização do trigger e região do Cloud Run em conjunto, pois isso afeta latência, residência de dados e preço.
Logs e verificação
Após o deploy, teste o endpoint e leia os logs antes de considerar a função pronta.
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
Registre o suficiente para investigar: ID do evento, nome do arquivo, flag de duplicidade, status de API externa e IDs de negócio seguros. Não registre tokens, corpos completos de mensagens ou dados pessoais.
Armadilhas comuns
Primeiro, retries sem idempotência criam efeitos duplicados. Cobranças, e-mails, estoque e importações precisam de um ID de evento ou chave de negócio persistida.
Segundo, o acesso ao Secret Manager deve estar na conta de execução. O deploy pode passar e as requisições falharem com Permission denied.
Terceiro, função HTTP pública precisa de mais do que --allow-unauthenticated: assinatura, rate limit e, se fizer sentido, API Gateway ou Cloud Armor. Para jobs internos, prefira chamadas autenticadas.
Quarto, Cloud Run functions não deve virar hospedagem de aplicação completa. Muitas rotas, jobs longos, pacotes de sistema, GPU ou controle fino de contêiner combinam melhor com Cloud Run, Cloud Run jobs ou Workflows.
Quinto, inclua custo e limpeza no prompt. Mesmo com scale-to-zero, Artifact Registry, Cloud Build, Storage, Eventarc e Cloud Logging podem gerar custos.
Prompt de revisão
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.
A ClaudeCodeLab transforma essas checklists operacionais em produtos e templates e treinamentos para equipes. Isso ajuda quando você quer revisões serverless repetíveis, e não dependentes da memória de uma única pessoa sênior.
Documentação oficial
- Cloud Run functions documentation
- Deploy a Cloud Run function
- Deploy a Cloud Run function using the gcloud CLI
- Write Cloud Run functions
- Local functions development
- Trigger functions from Cloud Storage using Eventarc
- Configure secrets for Cloud Run services
- Logging and viewing logs in Cloud Run
- Compare Cloud Run functions
- Cloud Run locations
Resultado
A forma testada é pequena, mas próxima da produção: Cloud Run functions com Node.js 24, comandos locais do Functions Framework, verificações HTTP e CloudEvent com curl, injeção via Secret Manager, idempotência com Firestore e consultas no Cloud Logging. O hábito importante é revisar identidade, logs, retries, região e custos antes de liberar tráfego real.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Workflow Obsidian para CLAUDE.md com Claude Code
Transforme notas de trabalho do Obsidian em notas operacionais CLAUDE.md para preservar contexto.
Claude Code Revenue CTA Routing: artigos para PDF, Gumroad e consultoria
Um fluxo com Claude Code para levar leitores ao PDF grátis, Gumroad ou consultoria conforme intenção.
Regras de handoff para equipes com Claude Code: evidências, permissões, rollback e receita
Formato prático para entregar trabalho do Claude Code com prova, permissões, rollback, PDF grátis, Gumroad e consultoria.