Cloudflare Workers avec Claude Code : guide pratique d'API edge
Créez une API Workers avec Claude Code, Wrangler, KV/D1/R2, cache, rate limiting, logs et en-têtes de sécurité.
Cloudflare Workers exécute JavaScript et TypeScript sur le réseau edge de Cloudflare. Edge signifie que le code tourne au plus près de l’utilisateur, au lieu de revenir systématiquement vers une seule région. C’est adapté aux petites API, webhooks, BFF, réponses en cache, contrôles de sécurité et téléchargements protégés depuis R2.
Claude Code est utile ici parce qu’un Worker se découpe proprement : un fetch handler, un fichier wrangler.toml et des bindings explicites comme KV, D1 et R2. J’ai revérifié les docs officielles le 3 juin 2026 : Workers, Wrangler, bindings, KV, D1, R2, Cache API, Rate Limiting et Workers Logs. Pour comparer, lisez aussi serverless functions et AWS Lambda guide.
Ce que nous construisons
L’exemple est une API de commandes. D1 stocke les commandes, KV garde des réglages simples, R2 reçoit les reçus JSON, Cache API accélère les GET sûrs, Rate Limiting bloque les abus, Workers Logs conserve des logs structurés et toutes les réponses reçoivent des en-têtes de sécurité.
flowchart LR
Client["Client"] --> Worker["Worker fetch handler"]
Worker --> D1["D1 commandes"]
Worker --> KV["KV réglages"]
Worker --> R2["R2 reçus"]
Worker --> Cache["Cache API"]
Worker --> Logs["Workers Logs"]
Un binding est une capacité externe injectée dans le Worker. Si wrangler.toml déclare binding = "DB", le code utilise env.DB.
Prompt pour Claude Code
Implémente une API de commandes avec Cloudflare Workers + TypeScript.
Fichiers:
- src/index.ts
- wrangler.toml
- schema.sql
Exigences:
- Utiliser le format module fetch handler
- GET /health retourne du JSON
- GET /orders/:id lit D1 et ne met en cache que la sortie publique sûre pendant 30 secondes
- POST /orders valide le JSON et insère dans D1
- Vérifier Authorization Bearer avec API_TOKEN
- Utiliser les bindings SETTINGS KV, DB D1, RECEIPTS R2 et API_RATE_LIMITER
- Ajouter des security headers à toutes les réponses
- Écrire les logs sous forme d'objets JSON
- Inclure des commandes curl de vérification
Interdits:
- Mettre des secrets dans wrangler.toml
- Supposer un serveur Node.js persistant
- Fournir du pseudocode
Configuration Wrangler
npm create cloudflare@latest claude-worker-api -- --type=hello-world
cd claude-worker-api
npm install -D typescript wrangler
npx wrangler --version
C3 peut générer wrangler.jsonc dans les nouveaux projets. Wrangler prend en charge JSON/JSONC et TOML ; cet article utilise TOML pour la lisibilité. Si votre projet a déjà wrangler.jsonc, gardez les mêmes clés en 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
Schéma 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 exécutable
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>;
Test local et déploiement
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
Cas d’usage
Premier cas : formulaire ou commande. D1 garde l’état, R2 garde les pièces jointes ou reçus, Workers Logs aide au diagnostic.
Deuxième cas : récepteur webhook. Vérifiez la signature, stockez l’event ID dans D1 et ignorez les doublons.
Troisième cas : BFF, c’est-à-dire Backend for Frontend. Le Worker cache les clés API, transforme la réponse pour l’interface et ne met en cache que les données sûres.
Quatrième cas : téléchargement contrôlé depuis R2. Le fichier lourd reste dans R2, tandis que l’autorisation et les logs restent dans le Worker.
Pièges fréquents
N’écrivez pas un Worker comme un serveur Node.js persistant. Utilisez les Web APIs, Request, Response, fetch, crypto et les bindings.
Ne mélangez pas les rôles de stockage. KV est fait pour de petites paires clé-valeur, D1 pour les données relationnelles, R2 pour les objets et Cache API pour des réponses courtes.
Ne mettez pas en cache des réponses privées. Si la réponse dépend d’un cookie, d’un token, d’un email ou d’un utilisateur, évitez le cache partagé.
Ne limitez pas seulement par IP. Préférez API key, user ID ou tenant ID.
Quel service choisir
Choisissez Workers pour une API HTTP à faible latence proche du cache Cloudflare et des bindings. Choisissez Pages Functions pour ajouter un peu de logique dynamique à un site Cloudflare Pages. Choisissez Cloud Run pour des conteneurs, des traitements longs ou un framework complet. Choisissez Lambda si le flux est centré sur AWS, par exemple S3, DynamoDB, EventBridge ou SQS.
Prompt de revue
Passe en revue cette implémentation Cloudflare Workers.
Vérifie:
- compatibilité Workers runtime
- cohérence entre Env et bindings Wrangler
- fuite de secrets dans code, logs ou config
- usage de bind D1 et risque SQL injection
- usage dangereux de Cache API
- choix de clé Rate Limiting
- security headers sur toutes les réponses
- étapes curl manquantes
Retourne les problèmes par sévérité, puis les corrections et les tests.
CTA et note de vérification
Migrez d’abord un seul endpoint : /health, un GET en lecture seule ou un webhook. Donnez à Claude Code les fichiers, bindings, commandes de vérification et interdits. Pour des modèles réutilisables, consultez products ; pour former une équipe aux revues et à l’exploitation, consultez training.
Résultat après essai réel : J’ai vérifié le flux de cet article comme chaîne Wrangler, D1, Worker et curl ; avant production, refaites wrangler deploy, wrangler tail, les en-têtes, le cache et la réponse 429 dans votre compte Cloudflare.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Workflow Obsidian vers CLAUDE.md avec Claude Code
Transformer des notes Obsidian en notes CLAUDE.md concises pour reprendre les sessions sans réexpliquer.
Claude Code Revenue CTA Routing : relier articles, PDF, Gumroad et consultation
Un workflow Claude Code pour orienter les lecteurs vers PDF gratuit, Gumroad ou consultation selon l'intention.
Règles de handoff Claude Code en équipe: preuves, permissions, rollback et revenus
Un format concret pour transmettre un travail Claude Code avec preuves, permissions, rollback, PDF gratuit, Gumroad et consultation.