用 Claude Code 实战 Cloudflare Workers 边缘 API
用Claude Code实现Workers API:Wrangler、KV/D1/R2、缓存、限流、日志和安全响应头。
Cloudflare Workers 可以把 JavaScript/TypeScript 运行在 Cloudflare 的边缘网络上。所谓边缘,就是尽量靠近用户的位置执行代码,而不是所有请求都回到单一区域。它适合轻量 API、Webhook、BFF、缓存响应、安全检查和文件下载控制。
Claude Code 适合这类工作,因为 Workers 项目通常由清晰的 fetch 入口、wrangler.toml 配置和显式 binding 组成。本文已在 2026 年 6 月 3 日复核官方文档;实现前建议打开 Workers、Wrangler、bindings、KV、D1、R2、Cache API、Rate Limiting 和 Workers Logs。内部延伸阅读可参考 serverless functions 与 AWS Lambda guide。
本文要做什么
我们实现一个订单 API:D1 保存订单,KV 保存小配置,R2 保存收据 JSON,Cache API 缓存安全的 GET 响应,Rate Limiting 限制频率,Workers Logs 记录结构化日志,并为所有响应加安全头。
flowchart LR
Client["客户端"] --> Worker["Worker fetch handler"]
Worker --> D1["D1 订单表"]
Worker --> KV["KV 设置"]
Worker --> R2["R2 收据"]
Worker --> Cache["Cache API"]
Worker --> Logs["Workers Logs"]
binding 可以理解成“把外部能力注入 Worker 的变量”。例如 binding = "DB" 会让代码通过 env.DB 访问 D1。
给 Claude Code 的提示词
请用 Cloudflare Workers + TypeScript 实现订单 API。
文件:
- src/index.ts
- wrangler.toml
- schema.sql
要求:
- 使用 module fetch handler
- GET /health 返回 JSON
- GET /orders/:id 从 D1 读取,并只缓存安全公开输出 30 秒
- POST /orders 校验 JSON 后写入 D1
- 用 Authorization Bearer 和 API_TOKEN 校验
- 使用 SETTINGS KV、DB D1、RECEIPTS R2、API_RATE_LIMITER binding
- 每个响应都带 security headers
- console.log 输出 JSON 对象
- 提供 curl 验证命令
禁止:
- 把 secret 写进 wrangler.toml
- 假设有长期运行的 Node.js server
- 用伪代码代替实现
Wrangler 配置
npm create cloudflare@latest claude-worker-api -- --type=hello-world
cd claude-worker-api
npm install -D typescript wrangler
npx wrangler --version
C3 新项目可能生成 wrangler.jsonc。Wrangler 支持 JSON/JSONC 和 TOML 配置文件;本文为了可读性使用 TOML。如果你的项目已经使用 wrangler.jsonc,保持同样的 key,用 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
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
可运行的 src/index.ts
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>;
本地验证与部署
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
真实使用场景
第一个场景是表单或订单 API。D1 存记录,R2 存附件或收据,日志保留排查线索。
第二个场景是 Webhook 接收端。验证签名,把事件 ID 存入 D1,重复事件直接跳过。
第三个场景是 BFF,也就是面向前端的轻量后端。Worker 隐藏上游 API key,整理响应格式,并缓存安全数据。
第四个场景是 R2 文件下载控制。大文件放 R2,授权、日志和响应头由 Worker 处理。
这些场景共同点是请求边界很清楚:收到请求、校验、读写少量资源、快速返回。让 Claude Code 处理这类任务时,不要只说“做一个后端”,而要说明每个 endpoint 的输入、输出、binding 和验证命令。这样生成的差分更容易 review,也更容易用 curl 复现。
如果任务变重,例如图片转码、PDF 生成、长时间 AI 推理、批量同步,就不要让 Worker 在一次请求里全部做完。更稳妥的做法是让 Worker 负责认证、排队、记录状态,再把慢任务交给 Cloud Run、Lambda、Queues 或其他后台服务。这个边界要在实现前就让 Claude Code 解释清楚。
常见坑
不要把 Workers 当成长期运行的 Node.js server。优先使用 Web API、Request、Response、fetch、crypto 和 binding。
不要混用存储职责。KV 放小配置,D1 放关系数据,R2 放对象文件,Cache API 做短期响应加速。
不要缓存私人响应。包含 cookie、token、邮箱或用户专属数据的响应,不应进入共享缓存。
不要只按 IP 限流。公司网络和移动网络会共享 IP,最好用 API key、user ID 或 tenant ID。
还要注意环境变量的边界。.dev.vars 适合本地开发,生产 secret 必须用 wrangler secret put 或 Cloudflare dashboard 管理。wrangler.toml 里的 vars 只能放公开配置,不要放 token、cookie secret 或 webhook secret。
日志也要按生产数据对待。Workers Logs 很适合排查问题,但如果把 Authorization header、cookie、完整请求体或客户邮箱直接打进去,后续就会变成可搜索的敏感数据。建议只记录 requestId、route、状态码、orderId 和粗粒度错误类型。
如何选择平台
低延迟 HTTP API、Cloudflare cache 和 bindings 优先选 Workers。Cloudflare Pages 站点旁边只需要少量动态逻辑时选 Pages Functions。需要容器、完整框架或长任务时选 Cloud Run。工作流围绕 AWS S3、DynamoDB、EventBridge、SQS 时选 Lambda。
Claude Code 审查提示词
请审查这个 Cloudflare Workers 实现。
检查:
- 是否兼容 Workers runtime
- Env 类型和 wrangler binding 名称是否一致
- secret 是否泄漏到代码、日志或配置
- D1 是否使用 bind,是否有 SQL injection 风险
- Cache API 是否缓存了不安全响应
- Rate Limiting key 是否合理
- 每个响应是否都有 security headers
- curl 验证步骤是否完整
按严重程度输出问题、修复建议和测试建议。
CTA 与实测记录
迁移时先选一个端点:/health、只读 GET 或 Webhook。把文件范围、binding 名、验证命令和禁止事项交给 Claude Code。可复用模板请看 products;团队培训和代码审查习惯请看 training。
实际试用结果:本文示例按 Wrangler、D1、Worker、curl 的顺序串联检查过;上线前请在自己的 Cloudflare 账户里重新确认 wrangler deploy、wrangler tail、响应头、缓存命中和 429 行为。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。