Use Cases (Atualizado: 03/06/2026)

Cloudflare Workers com Claude Code: guia prático de API edge

Crie uma API Workers com Claude Code, Wrangler, KV/D1/R2, cache, rate limiting, logs e headers seguros.

Cloudflare Workers com Claude Code: guia prático de API edge

Cloudflare Workers executa JavaScript e TypeScript na rede edge da Cloudflare. Edge quer dizer que o código roda perto do usuário, não apenas em uma região central. Isso combina com APIs pequenas, webhooks, BFFs, respostas em cache, checagens de segurança e controle de downloads no R2.

Claude Code funciona bem nesse cenário porque um Worker tem partes claras: um fetch handler, um wrangler.toml e bindings explícitos como KV, D1 e R2. As docs oficiais foram revisadas em 3 de junho de 2026; consulte Workers, Wrangler, bindings, KV, D1, R2, Cache API, Rate Limiting e Workers Logs. Para comparar, veja serverless functions e AWS Lambda guide.

O que vamos construir

O exemplo é uma API de pedidos. D1 guarda pedidos, KV lê configurações simples, R2 armazena recibos JSON, Cache API acelera GETs seguros, Rate Limiting controla abuso, Workers Logs guarda logs estruturados e toda resposta recebe headers de segurança.

flowchart LR
  Client["Cliente"] --> Worker["Worker fetch handler"]
  Worker --> D1["D1 pedidos"]
  Worker --> KV["KV configs"]
  Worker --> R2["R2 recibos"]
  Worker --> Cache["Cache API"]
  Worker --> Logs["Workers Logs"]

Um binding é uma capacidade externa injetada no Worker. Se wrangler.toml declara binding = "DB", o código acessa env.DB.

Prompt para Claude Code

Implemente uma API de pedidos com Cloudflare Workers + TypeScript.

Arquivos:
- src/index.ts
- wrangler.toml
- schema.sql

Requisitos:
- Usar module fetch handler
- GET /health retorna JSON
- GET /orders/:id lê D1 e cacheia apenas saída pública segura por 30 segundos
- POST /orders valida JSON e insere no D1
- Validar Authorization Bearer contra API_TOKEN
- Usar SETTINGS KV, DB D1, RECEIPTS R2 e API_RATE_LIMITER
- Adicionar security headers a toda resposta
- Registrar logs como objetos JSON
- Incluir comandos curl de verificação

Não faça:
- Escrever secrets no wrangler.toml
- Assumir servidor Node.js persistente
- Entregar pseudocódigo

Setup com Wrangler

npm create cloudflare@latest claude-worker-api -- --type=hello-world
cd claude-worker-api
npm install -D typescript wrangler
npx wrangler --version

C3 pode gerar wrangler.jsonc em projetos novos. Wrangler aceita JSON/JSONC e TOML; este artigo usa TOML por legibilidade. Se o seu projeto já tem wrangler.jsonc, mantenha as mesmas chaves em JSONC.

name = "claude-worker-api"
main = "src/index.ts"
compatibility_date = "2026-06-03"

[vars]
PUBLIC_ENV = "production"

[observability]
enabled = true
head_sampling_rate = 1

[[d1_databases]]
binding = "DB"
database_name = "claude-worker-api"
database_id = "replace-with-d1-database-id"

[[kv_namespaces]]
binding = "SETTINGS"
id = "replace-with-kv-namespace-id"

[[r2_buckets]]
binding = "RECEIPTS"
bucket_name = "claude-worker-receipts"

[[ratelimits]]
name = "API_RATE_LIMITER"
namespace_id = "1001"

  [ratelimits.simple]
  limit = 60
  period = 60
npx wrangler login
npx wrangler d1 create claude-worker-api
npx wrangler kv namespace create SETTINGS
npx wrangler r2 bucket create claude-worker-receipts
npx wrangler secret put API_TOKEN

Schema D1

CREATE TABLE IF NOT EXISTS orders (
  id TEXT PRIMARY KEY,
  email TEXT NOT NULL,
  amount INTEGER NOT NULL,
  status TEXT NOT NULL DEFAULT 'pending',
  created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_orders_email ON orders(email);
npx wrangler d1 execute claude-worker-api --local --file=./schema.sql
npx wrangler d1 execute claude-worker-api --remote --file=./schema.sql

Worker executável

export interface Env {
  API_TOKEN: string;
  PUBLIC_ENV: string;
  DB: D1Database;
  SETTINGS: KVNamespace;
  RECEIPTS: R2Bucket;
  API_RATE_LIMITER: RateLimit;
}

const securityHeaders = {
  "content-security-policy": "default-src 'none'; frame-ancestors 'none'",
  "x-content-type-options": "nosniff",
  "x-frame-options": "DENY",
  "referrer-policy": "no-referrer",
  "permissions-policy": "camera=(), microphone=(), geolocation=()",
};

function json(data: unknown, init: ResponseInit = {}) {
  return new Response(JSON.stringify(data), {
    ...init,
    headers: {
      "content-type": "application/json; charset=utf-8",
      ...securityHeaders,
      ...init.headers,
    },
  });
}

export default {
  async fetch(request, env, ctx): Promise<Response> {
    const url = new URL(request.url);
    const requestId = crypto.randomUUID();
    console.log({ event: "request_started", requestId, method: request.method, path: url.pathname });

    if (url.pathname === "/health") {
      const maintenance = await env.SETTINGS.get("maintenance");
      return json({ ok: true, env: env.PUBLIC_ENV, maintenance: maintenance === "true" });
    }

    if (request.headers.get("authorization") !== `Bearer ${env.API_TOKEN}`) {
      return json({ error: "unauthorized" }, { status: 401 });
    }

    const { success } = await env.API_RATE_LIMITER.limit({
      key: request.headers.get("authorization")?.slice(-16) ?? "anonymous",
    });
    if (!success) return json({ error: "rate_limited" }, { status: 429 });

    const match = url.pathname.match(/^\/orders\/([a-zA-Z0-9_-]+)$/);

    if (request.method === "GET" && match) {
      const cacheKey = new Request(url.toString(), { method: "GET" });
      const cached = await caches.default.match(cacheKey);
      if (cached) return cached;

      const order = await env.DB.prepare(
        "SELECT id, email, amount, status, created_at FROM orders WHERE id = ?"
      ).bind(match[1]).first();
      if (!order) return json({ error: "not_found" }, { status: 404 });

      const response = json({ order }, {
        headers: { "cache-control": "public, max-age=30", "cache-tag": `order-${match[1]}` },
      });
      ctx.waitUntil(caches.default.put(cacheKey, response.clone()));
      return response;
    }

    if (request.method === "POST" && url.pathname === "/orders") {
      const body = await request.json<{ email?: string; amount?: number }>();
      if (!body.email?.includes("@") || !Number.isInteger(body.amount) || body.amount <= 0) {
        return json({ error: "invalid_order" }, { status: 400 });
      }

      const id = crypto.randomUUID();
      await env.DB.prepare(
        "INSERT INTO orders (id, email, amount, status) VALUES (?, ?, ?, ?)"
      ).bind(id, body.email, body.amount, "pending").run();

      await env.RECEIPTS.put(`orders/${id}.json`, JSON.stringify({ id, email: body.email, amount: body.amount }), {
        httpMetadata: { contentType: "application/json" },
      });

      console.log({ event: "order_created", requestId, orderId: id });
      return json({ id, status: "pending" }, { status: 201 });
    }

    return json({ error: "not_found" }, { status: 404 });
  },
} satisfies ExportedHandler<Env>;

Teste local e deploy

printf "API_TOKEN=dev-token\n" > .dev.vars
npx wrangler dev
curl http://localhost:8787/health

curl -X POST http://localhost:8787/orders \
  -H "authorization: Bearer dev-token" \
  -H "content-type: application/json" \
  -d "{\"email\":\"masa@example.com\",\"amount\":1200}"

curl http://localhost:8787/orders/replace-with-created-id \
  -H "authorization: Bearer dev-token"
npx wrangler secret put API_TOKEN
npx wrangler d1 execute claude-worker-api --remote --file=./schema.sql
npx wrangler deploy
npx wrangler tail

Casos de uso

Primeiro, formulários e pedidos. D1 guarda estado, R2 guarda recibos ou anexos e Workers Logs ajuda na operação.

Segundo, receptor de webhooks. Valide assinatura, salve o event ID em D1 e ignore duplicatas.

Terceiro, BFF, ou Backend for Frontend. O Worker esconde API keys, adapta respostas para a UI e cacheia apenas dados seguros.

Quarto, entrega controlada de arquivos via R2. O arquivo pesado fica no R2; autorização e logs ficam no Worker.

Armadilhas

Não escreva Workers como servidores Node.js persistentes. Use Web APIs, Request, Response, fetch, crypto e bindings.

Não misture papéis de storage. KV é para chave-valor pequeno, D1 para dados relacionais, R2 para objetos e Cache API para respostas curtas.

Não cacheie respostas privadas. Se dependem de cookie, token, email ou usuário, evite cache compartilhado.

Não faça rate limit só por IP. Prefira API key, user ID ou tenant ID.

Escolha da plataforma

Use Workers para APIs HTTP de baixa latência perto do cache e dos bindings da Cloudflare. Use Pages Functions quando um site Cloudflare Pages precisa de pouca lógica dinâmica. Use Cloud Run para contêineres, tarefas longas ou frameworks completos. Use Lambda quando o fluxo gira em torno de AWS S3, DynamoDB, EventBridge ou SQS.

Prompt de revisão

Revise esta implementação de Cloudflare Workers.

Verifique:
- compatibilidade com Workers runtime
- alinhamento entre Env e bindings do Wrangler
- vazamento de secrets em código, logs ou config
- uso de bind no D1 e risco de SQL injection
- uso inseguro da Cache API
- chave de Rate Limiting
- security headers em toda resposta
- passos curl ausentes

Retorne achados por severidade, correções e testes.

CTA e nota de verificação

Migre primeiro apenas um endpoint: /health, um GET somente leitura ou um webhook. Dê ao Claude Code arquivos, bindings, comandos de verificação e proibições. Para templates reutilizáveis, veja products; para treinar o time em revisão e operação, veja training.

Resultado do teste prático: Eu conectei o fluxo do artigo como Wrangler, D1, Worker e curl; antes de produção, valide wrangler deploy, wrangler tail, headers, cache e resposta 429 na sua conta Cloudflare.

#Claude Code #Cloudflare Workers #edge computing #serverless #API
Grátis

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.

Masa

Sobre o autor

Masa

Engenheiro focado em workflows práticos com Claude Code.