前言¶
写 Cloudflare Workers 时,代码表面上很像普通 TypeScript:一个 fetch 处理器、几次 await、再 return new Response(...)。真正上线之后,问题往往出在运行时约定上,而不是语法。对未知大小的响应体 await response.text(),会把 Worker 的内存打满;模块顶层用 let 缓存当前用户,下一请求还能看见;随手写一个没有 await 的 fetch(),isolate 可能在 Promise 跑完前就被回收。这些写法在 Node.js 服务里常常只是「不太优雅」,在 Workers 里会变成串数据、吞错误,或者直接崩溃。
另一边,团队里的 PR Review 也很容易变成同一张清单的重复劳动:compatibility_date 是不是太旧、密钥有没有写进 vars、Env 是手写的还是 wrangler types 生成的、有没有开 observability。AI 编程助手如果只靠训练数据,更容易把 Node.js 习惯搬到边缘上,给出过时的绑定类型或已经不推荐的配置字段。
Cloudflare 官方在 cloudflare/skills 仓库里维护了一条专项 Skill,名字就叫 workers-best-practices。它做的不是再讲一遍 Workers 入门,而是把「先查现行文档,再按清单写代码 / 审代码」收成 Agent 可执行的工作流,覆盖流式处理、floating promise、全局状态、密钥、绑定和 wrangler 配置。
这是什么¶
workers-best-practices 是 Cloudflare 官方出品的 Agent Skill,目录在:
https://github.com/cloudflare/skills/tree/main/skills/workers-best-practices
SKILL.md 头部的定位很直接:按生产最佳实践审查与编写 Cloudflare Workers 代码;在写新 Worker、审查已有代码、配置 wrangler.jsonc,或检查常见反模式(streaming、floating promises、global state、secrets、bindings、observability)时加载。同时写明:偏向从 Cloudflare 文档检索,而不是依赖模型的预训练知识。
它要解决的是两件很具体的事:
- Workers 的坑和 Node.js 看起来一样,运行时行为却不一样。Agent 若按通用后端经验写,会漏掉 isolate 复用、内存上限、
ctx绑定这些约束。 - API 签名、兼容日期和 wrangler 字段会变。Skill 要求审查或生成代码之前,先拉现行最佳实践页、
@cloudflare/workers-types和本地config-schema.json。
同一仓库里还有总索引 Skill cloudflare、偏有状态协调的 durable-objects,以及偏 CLI 与资源管理的 wrangler。任务已经收敛到「写 Worker / 审 Worker / 查 wrangler 配置是否符合现行约定」时,应走 workers-best-practices 这一条。
Skill 目录是入口加两份按需参考:
skills/workers-best-practices/
├── SKILL.md
└── references/
├── rules.md # 规则、正确写法与反模式
└── review.md # 类型检查、配置校验、绑定访问、审查流程
正文要求动手前先取现行资料,而不是把参考文件当最终 API 手册:
| 来源 | 怎么取 | 用来干什么 |
|---|---|---|
| Workers 最佳实践 | 拉取 https://developers.cloudflare.com/workers/best-practices/workers-best-practices/ |
规则、模式、反模式 |
| Workers 类型 | 见 references/review.md |
API 签名、handler、绑定类型 |
| Wrangler schema | node_modules/wrangler/config-schema.json |
配置字段、绑定形状、允许值 |
| Cloudflare 文档 | 搜索或 https://developers.cloudflare.com/workers/ |
API、兼容日期与 flag |
项目 node_modules 里的类型包如果偏旧,Skill 要求优先用最新发布版本。取类型的命令写在入口文件里:
mkdir -p /tmp/workers-types-latest && \
npm pack @cloudflare/workers-types --pack-destination /tmp/workers-types-latest && \
tar -xzf /tmp/workers-types-latest/cloudflare-workers-types-*.tgz -C /tmp/workers-types-latest
# Types at /tmp/workers-types-latest/package/index.d.ts
核心能力¶
Skill 把规则收成配置、请求响应、架构、可观测性、代码模式、安全几类,细节在 references/rules.md。下面按官方清单说明 Agent 加载之后应当检查什么。
1、配置:日期、兼容、类型和密钥¶
新项目要把 compatibility_date 设成当天日期,已有项目定期更新。nodejs_compat 要打开,很多库依赖 node:crypto、node:buffer、node:stream,缺了这个 flag,运行时的 import 错误会很难看懂。绑定类型不要手写 interface Env,用 wrangler types 从配置生成,加绑定或改名之后再跑一次。密钥走 wrangler secret put,不要写进配置或源码;非密钥配置放 vars。新项目优先 wrangler.jsonc,Skill 写明较新的功能是 JSON-only,JSONC 还可以给配置决策加注释。
最小形态接近下面这样:
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2026-08-16",
"compatibility_flags": ["nodejs_compat"],
"vars": {
"API_BASE_URL": "https://api.example.com"
}
// Secrets set via: wrangler secret put API_KEY
}
compatibility_date 应换成运行当天的日期。官方最佳实践页上的示例日期会随文档更新,不要把它抄成固定值。
生成类型:
npx wrangler types
放入密钥:
npx wrangler secret put API_KEY
对应的代码侧,Skill 推荐用生成出来的 Env,并用 satisfies ExportedHandler<Env> 校验导出,而不是自己维护一份绑定接口:
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const value = await env.MY_KV.get("key");
return new Response(value);
},
} satisfies ExportedHandler<Env>;
2、请求响应:流式,以及响应后的工作¶
Workers 有 128 MB 内存上限。对可能很大、长度未知的数据调用 await response.text()、await response.json() 或 await response.arrayBuffer(),会把整个 body 读进内存。已知大小、有界的 JSON 可以缓冲;大文件或上游大数据集应把 response.body 直接传下去,或用 TransformStream 管道。官方文档和 Skill 给的正确写法是同一类:
async fetch(request: Request, env: Env): Promise<Response> {
const response = await fetch("https://api.example.com/large-dataset");
return new Response(response.body, response);
}
响应已经可以返回、但还有分析、写缓存、打 webhook 这类收尾工作时,用 ctx.waitUntil(),不要把它们 await 在返回之前。Skill 和官方文档都强调:不要解构 ctx。const { waitUntil } = ctx 会丢掉 this,运行时抛 Illegal invocation。waitUntil 在响应发出后大约有 30 秒窗口,具体以现行文档为准。
3、架构:绑定优先,后台工作离开热路径¶
KV、R2、D1、Queues、Workflows 应走进程内绑定,不要在 Worker 里再调 https://api.cloudflare.com/client/v4/...。Worker 之间用 service binding(RPC 或 env.SERVICE.fetch()),不要走公网 URL。外部 PostgreSQL / MySQL 走 Hyperdrive,并且每个请求 new Client(),连接池由 Hyperdrive 管;这一条依赖 nodejs_compat。
长任务、可重试任务、不挡响应的任务,从 fetch 热路径挪到 Queues 或 Workflows:
- Queues:解耦生产与消费,扇出、缓冲、单步后台任务,至少一次投递。
- Workflows:多步持久执行,每步返回值会落盘,失败只重试失败的那一步,可以跑很久。
Workflow 的专项规则不在这条 Skill 里,入口指向 Rules of Workflows。
4、可观测性与代码模式¶
上生产前在 wrangler 配置里打开 observability,用 head_sampling_rate 控制日志和 trace 的量。日志用结构化 JSON,console.error 才会在控制台里落到 error 级别。
{
"observability": {
"enabled": true,
"logs": { "head_sampling_rate": 1 },
"traces": { "enabled": true, "head_sampling_rate": 0.01 }
}
}
console.log(JSON.stringify({
message: "incoming request",
method: request.method,
path: url.pathname,
}));
代码模式里有两条几乎每次审查都会碰到:
- 不要把请求态放进模块全局。 isolate 会跨请求复用,模块级
let currentUser会造成串数据、过期状态,以及Cannot perform I/O on behalf of a different request。 - 每个 Promise 都要有归属。 需要结果就
await或return;不挡响应就交给ctx.waitUntil();也可以显式void。裸fetch()是 floating promise:结果丢掉、错误被吞,isolate 还可能提前结束。Skill 要求用@typescript-eslint/no-floating-promises或 oxlint 的同类规则扫一遍。
5、安全,以及一张反模式表¶
安全相关的检查很具体:令牌和 ID 用 crypto.randomUUID() / crypto.getRandomValues(),不要用 Math.random();比较密钥用 crypto.subtle.timingSafeEqual(),先哈希到固定长度,避免按长度短路径返回。出错时显式 try/catch 并返回结构化错误,不要把 ctx.passThroughOnException() 当成错误处理——它会在 Worker 抛错时把请求交给源站,把 bug 藏起来。
SKILL.md 里的反模式表,是审查时真正会逐条打的清单。和官方最佳实践页能对上的包括:
| 反模式 | 为什么要紧 |
|---|---|
对无界数据 await response.text() |
内存打满,128 MB 上限 |
| 源码或配置里写死密钥 | 进版本库就泄漏 |
用 Math.random() 当令牌 / ID |
可预测,不是密码学安全 |
裸 fetch() 不 await 也不 waitUntil |
floating promise |
| 模块级可变变量保存请求态 | 跨请求串数据 |
| Worker 内调 Cloudflare REST API | 多余网络跳、鉴权和延迟 |
ctx.passThroughOnException() 当错误处理 |
藏 bug |
手写 Env |
和真实绑定漂移 |
密钥用 === 比较 |
时序侧信道 |
解构 ctx |
Illegal invocation |
Env 或 handler 参数写成 any |
绑定访问失去类型 |
as unknown as T |
把不兼容藏起来 |
对平台基类用 implements 而不是 extends |
丢掉 this.ctx / this.env |
平台基类里写 env.X |
类里应是 this.env.X |
references/review.md 还补了序列化边界:Queue 消息、Workflow 步骤返回值、Durable Object 存储、postMessage() 必须能 structured clone。Response、Error、带方法的类实例、Map / Set 看起来能编译,运行时会失败。
安装与启用¶
官方 README 写明:这套 Skill 面向支持 Agent Skills 标准的助手,包括 Claude Code、Cursor、OpenCode、OpenAI Codex 和 Pi。安装方式按工具分开,不要混用。
需要如实说一句:仓库 README 的 Skills 表目前列了 cloudflare、durable-objects、wrangler 等,没有单独列出 workers-best-practices。GitHub 上 skills/ 目录里这条是存在的,YAML 描述也会在「审查 Worker / 写 wrangler.jsonc / 查反模式」时被自动加载。装整套集合就会带上它;只装这一条时,用下面带 --skill 的命令。
1、用 npx skills(跨工具通用)
安装整个 Cloudflare Skills 集合:
npx skills add https://github.com/cloudflare/skills
只装这一条时,officialskills.sh 上的页面 和 skills.sh 给出的命令是:
npx skills add https://github.com/cloudflare/skills --skill workers-best-practices
2、Claude Code(插件市场)
/plugin marketplace add cloudflare/skills
/plugin install cloudflare@cloudflare
3、Cursor
仓库 README 的写法是:从 Cursor Marketplace 安装,或在 Settings > Rules > Add Rule > Remote Rule (Github) 里填 cloudflare/skills。Cloudflare 的 Cursor 接入文档 另外给出斜杠命令 /add-plugin cloudflare,效果是装上整套 Cloudflare Skills 并注册 MCP。
4、克隆后按目录拷贝
| 工具 | Skill 目录 |
|---|---|
| Claude Code | ~/.claude/skills/ |
| Cursor | ~/.cursor/skills/ |
| OpenCode | ~/.config/opencode/skills/ |
| OpenAI Codex | ~/.codex/skills/ |
| Pi | ~/.pi/agent/skills/ |
拷进去的应是 skills/workers-best-practices/ 这一整个文件夹,保证 SKILL.md 和 references/ 的相对路径还在。OpenAI 的 openai/plugins 仓库里也有一份同名 SKILL.md,内容和 Cloudflare 官方仓库一致,Codex 用户可能会从那边拿到。
Agent 一般按描述自动启用。提示词里出现「审查这段 Worker」「检查 floating promise」「帮我看 wrangler.jsonc」「这段代码有没有把请求态放全局」时,就会对上这条 Skill。
典型用法¶
下面几段都来自官方 SKILL.md 和两份参考文件,用来说明 Agent 加载之后应当怎么工作,而不是另编一套 Workers 教程。
1、先检索,再按清单审一整份文件¶
Skill 给的审查流程是固定的,顺序不要颠倒:
- Retrieve:拉最新最佳实践页、workers types、wrangler schema
- Read full files:不要只看 diff,绑定访问模式要看完整文件
- Check types:绑定访问、handler 签名、禁止
any和不安全断言 - Check config:
compatibility_date、nodejs_compat、observability、secrets、绑定与代码是否同名 - Check patterns:streaming、floating promises、全局状态、序列化边界
- Check security:Web Crypto、密钥、时序安全比较、错误处理
- Validate with tools:
npx tsc --noEmit,以及no-floating-promiseslint - Reference rules:每条意见对照
references/rules.md的正确写法
可以直接把触发条件写进提示词:
请按 workers-best-practices Skill 审查这个 Worker。
先拉取 Cloudflare Workers 现行最佳实践页、@cloudflare/workers-types 和 wrangler 的 config-schema.json,不要只靠训练数据。
重点看:无界 body 有没有被 text()/arrayBuffer() 整包读入、有没有 floating promise、模块全局有没有请求态、密钥有没有进源码、Env 是不是 wrangler types 生成的、ctx 有没有被解构。
读完整文件,不要只看 diff。给出文件名、行号和依据。
适合这条提示词的场景,和 Skill 自己的描述是对齐的:审 Worker PR、给把数据缓存在模块全局的代码做重构、核对 wrangler.jsonc 的绑定与可观测性、写新 Worker 时按现行约定落地。
2、审查意见的输出格式¶
references/review.md 要求意见带证据,不要只给「建议改进」这类空话:
**[SEVERITY]** Brief description
`file.ts:42` — explanation with evidence
Suggested fix: `code`
级别是 CRITICAL(安全、丢数据、崩溃)、HIGH(类型错误、错误 API、坏掉的配置)、MEDIUM(缺校验、边界情况)、LOW(风格、小改进)。Skill 自己的原则也写在入口里:先检索再下结论;引用行号、工具输出或文档链接;例子以开发者会复制进生产的那段为准;正确比面面俱到更重要。
3、几段会反复出现的对照代码¶
全局请求态,错误写法和正确写法:
// 反模式:模块级可变状态,请求之间会泄漏
let currentUser: string | null = null;
export default {
async fetch(request: Request, env: Env): Promise<Response> {
currentUser = request.headers.get("X-User-Id");
// ...
},
};
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const userId = request.headers.get("X-User-Id");
const result = await handleRequest(userId, env);
return Response.json(result);
},
} satisfies ExportedHandler<Env>;
响应后的收尾工作:
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const data = await processRequest(request);
ctx.waitUntil(logToAnalytics(env, data));
ctx.waitUntil(updateCache(env, data));
return Response.json(data);
}
绑定代替 REST:
const object = await env.MY_BUCKET.get("my-file");
工具校验,Skill 原文给出的命令是:
npx tsc --noEmit
npx eslint --rule '{"@typescript-eslint/no-floating-promises": "error"}' src/
npx oxlint --deny typescript/no-floating-promises src/
4、测试时不要被 Vitest 的自动注入骗过¶
rules.md 和官方最佳实践页都写了同一条坑:用 @cloudflare/vitest-pool-workers 可以在 Workers 运行时里测真实绑定,这是推荐做法;但这个 pool 会自动注入 nodejs_compat,所以测试能过,并不代表 wrangler.jsonc 里已经开了这个 flag。代码若依赖 Node.js 内置模块,配置里仍要显式写上。
适用场景与注意事项¶
比较适合这条 Skill 的情况:
- 正在写或重构 Cloudflare Workers,希望生成结果符合现行生产约定,而不是一份「能跑的 Node 风格代码」。
- 给 Worker PR 做审查,尤其是反复出现 floating promise、全局状态、密钥和 wrangler 配置问题的仓库。
- 要核对
wrangler.jsonc与代码里的绑定名、类型、observability 是否一致。 - 团队想把 Workers 特有的反模式固化成 Agent 可执行清单,减少同一类 Review 评论。
不太适合、或需要换 Skill 的情况,入口文件的 Scope 写得很清楚:
- Durable Objects:加载同仓的
durable-objects。 - Workflows:看 Rules of Workflows,不要把这条 Skill 当成 Workflow 手册。
- Wrangler CLI 命令(部署、建 KV/R2/D1 资源):加载
wrangler。 - 产品选型和全平台导航:用同仓的
cloudflare。 - 这条 Skill 是给编码助手的工作流,不会替你开通账号,也不包含计费决策。
使用时几条已经能交叉核实的约束,值得写进提示词:
- 先检索再下结论。 入口第一句就是:你对 Workers API、类型和配置的知识可能过时。
- 读完整文件。 绑定是
env.X还是this.env.X,只看 diff 经常看不出来。 - 平台基类用
extends。DurableObject、WorkerEntrypoint、Workflow上写implements是旧模式。 - CPU 和内存数字以现行文档为准。 Skill 会提醒去
/workers/platform/limits/核对;内存 128 MB 这一条目前和官方最佳实践页一致,计划档位的 CPU 限额不要死记。 - Vitest 通过不等于配置正确。 确认
wrangler.jsonc里真的有nodejs_compat。 - README 表格不是完整目录。 装集合或按路径拷贝时,以
skills/workers-best-practices/是否存在为准。
小结¶
workers-best-practices 这条 Skill 的价值,不在于把 Workers 文档再复制一遍,而在于把一张会过时的审查清单,收成 Agent 每次写代码、审 PR 时都要走的流程:先拉现行文档和类型,再核对配置、流式处理、Promise 归属、全局状态、密钥和可观测性。Cloudflare 把边缘运行时的坑写成反模式表之后,重复出现在 Review 里的那些评论,就可以交给会读 SKILL.md 的助手去打第一遍。
官方目录:https://github.com/cloudflare/skills/tree/main/skills/workers-best-practices
仓库说明与各工具安装方式:https://github.com/cloudflare/skills
最佳实践正文:https://developers.cloudflare.com/workers/best-practices/workers-best-practices/