Use Cases (Diperbarui: 3/6/2026)

Cloudflare Workers dengan Claude Code: panduan API edge praktis

Bangun API Workers dengan Claude Code, Wrangler, KV/D1/R2, cache, rate limit, logs, dan security headers.

Cloudflare Workers dengan Claude Code: panduan API edge praktis

Cloudflare Workers menjalankan JavaScript dan TypeScript di edge network Cloudflare. Edge berarti kode berjalan lebih dekat ke pengguna, bukan hanya di satu region pusat. Ini cocok untuk API kecil, webhook, BFF, response yang di-cache, pemeriksaan keamanan, dan kontrol download dari R2.

Claude Code cocok untuk pekerjaan ini karena struktur Worker jelas: fetch handler, wrangler.toml, dan binding eksplisit seperti KV, D1, dan R2. Dokumentasi resmi dicek ulang pada 3 Juni 2026; buka Workers, Wrangler, bindings, KV, D1, R2, Cache API, Rate Limiting, dan Workers Logs saat implementasi. Untuk perbandingan, lihat serverless functions dan AWS Lambda guide.

Yang akan dibuat

Contohnya adalah API pesanan. D1 menyimpan pesanan, KV membaca setting kecil, R2 menyimpan receipt JSON, Cache API menyimpan response GET yang aman untuk waktu singkat, Rate Limiting menahan abuse, Workers Logs mencatat log terstruktur, dan semua response memiliki security headers.

flowchart LR
  Client["Client"] --> Worker["Worker fetch handler"]
  Worker --> D1["D1 orders"]
  Worker --> KV["KV settings"]
  Worker --> R2["R2 receipts"]
  Worker --> Cache["Cache API"]
  Worker --> Logs["Workers Logs"]

Binding adalah kemampuan eksternal yang disuntikkan ke Worker. Jika wrangler.toml punya binding = "DB", kode memakai resource itu sebagai env.DB.

Prompt untuk Claude Code

Implementasikan API pesanan dengan Cloudflare Workers + TypeScript.

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

Syarat:
- Pakai format module fetch handler
- GET /health mengembalikan JSON
- GET /orders/:id membaca D1 dan hanya cache output publik yang aman selama 30 detik
- POST /orders validasi JSON lalu insert ke D1
- Validasi Authorization Bearer terhadap API_TOKEN
- Pakai binding SETTINGS KV, DB D1, RECEIPTS R2, API_RATE_LIMITER
- Tambahkan security headers ke semua response
- Log sebagai objek JSON
- Sertakan command curl untuk verifikasi

Jangan:
- Menaruh secret di wrangler.toml
- Mengasumsikan server Node.js yang berjalan terus
- Memberikan pseudocode

Setup Wrangler

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

C3 bisa membuat wrangler.jsonc pada project baru. Wrangler mendukung JSON/JSONC dan TOML; artikel ini memakai TOML agar mudah dibaca. Jika project Anda sudah memakai wrangler.jsonc, gunakan key yang sama dalam bentuk 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

Skema 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 yang bisa dijalankan

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>;

Tes lokal dan 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

Use case nyata

Pertama, form atau order API. D1 menyimpan status, R2 menyimpan receipt atau lampiran, dan Workers Logs membantu investigasi.

Kedua, penerima webhook. Verifikasi signature, simpan event ID di D1, lalu abaikan event duplikat.

Ketiga, BFF atau Backend for Frontend. Worker menyembunyikan API key, membentuk ulang response untuk UI, dan cache hanya data yang aman.

Keempat, pengiriman file dari R2. File besar tetap di R2; otorisasi dan log ditangani Worker.

Jebakan umum

Jangan menulis Workers seperti server Node.js yang berjalan terus. Gunakan Web APIs, Request, Response, fetch, crypto, dan bindings.

Jangan campur peran storage. KV untuk key-value kecil, D1 untuk data relasional, R2 untuk object, dan Cache API untuk response pendek.

Jangan cache response privat. Jika bergantung pada cookie, token, email, atau user, hindari shared cache.

Jangan rate limit hanya berdasarkan IP. Gunakan API key, user ID, atau tenant ID jika tersedia.

Memilih platform

Pilih Workers untuk API HTTP low-latency yang dekat dengan cache dan bindings Cloudflare. Pilih Pages Functions jika situs Cloudflare Pages hanya butuh sedikit logic dinamis. Pilih Cloud Run untuk container, job panjang, atau framework penuh. Pilih Lambda jika alurnya berpusat pada AWS S3, DynamoDB, EventBridge, atau SQS.

Prompt review Claude Code

Review implementasi Cloudflare Workers ini.

Cek:
- kompatibilitas Workers runtime
- kesesuaian Env type dan nama binding Wrangler
- secret bocor di code, logs, atau config
- penggunaan bind D1 dan risiko SQL injection
- penggunaan Cache API yang tidak aman
- pilihan key Rate Limiting
- security headers di semua response
- langkah curl yang belum ada

Kembalikan temuan berdasarkan severity, lalu perbaikan dan test.

CTA dan catatan verifikasi

Mulai dari satu endpoint saja: /health, GET read-only, atau webhook. Berikan Claude Code file, nama binding, command verifikasi, dan larangan yang jelas. Untuk template yang bisa dipakai ulang, lihat products; untuk pelatihan tim dan kebiasaan review, lihat training.

Hasil setelah dicoba langsung: Alur artikel ini sudah dirangkai sebagai Wrangler, D1, Worker, dan curl; sebelum produksi, cek ulang wrangler deploy, wrangler tail, headers, cache, dan response 429 di akun Cloudflare Anda.

#Claude Code #Cloudflare Workers #edge computing #serverless #API
Gratis

PDF gratis: cheatsheet Claude Code

Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.

Kami menjaga datamu dan tidak mengirim spam.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.