用 Rust 写 Cloudflare Workers:workers-rs + KV 实战指南
用一个「国家到城市」KV API 走通 workers-rs 实战流程:从 Rust/Wasm 环境、官方模板、Wrangler 本地调试,到 KV 绑定、部署和生产注意事项。
约二千一百字·读约六分钟 · English
为什么会想用 Rust 写 Worker
Cloudflare Workers 最常见的写法当然是 JavaScript 或 TypeScript:创建快、部署快,和 Web 平台也天然贴合。但如果你的项目里已经有 Rust 代码,或者你想把一些更偏系统层的逻辑放到边缘侧,workers-rs 就很值得看一眼。
它做的事情很直接:让你用 Rust 写 Worker,再编译成 WebAssembly,最后交给 Cloudflare Workers 运行时部署到全球边缘网络。换句话说,你依然在用 Workers 这套平台能力,只是把业务代码换成了 Rust。
我会在这些场景优先考虑它:
- 请求签名、协议解析、数据转换这类逻辑需要更强的类型约束。
- 团队已经熟悉 Rust,不想在边缘层再维护一套 JS 版本。
- 希望直接使用 Workers KV、R2、D1、Queues、Durable Objects、Workers AI 等绑定。
- 可以接受 Wasm 构建链路略复杂一点,换来更好的编译期检查。
这篇不追求把 workers-rs 所有能力讲完,而是先做一个能跑、能测、能部署的小项目:一个「国家 → 城市」存储 API,用 Workers KV 负责读写。
文中的命令和关键 API 已在 2026-07-30 按 Cloudflare 官方文档与 workers-rs 仓库核对。Wrangler、workers-rs 和 Workers 运行时都在持续更新,真正开新项目时,建议再对照一次官方当前文档。
先把环境准备好
workers-rs 的本质是 Rust + WebAssembly + Wrangler。所以开始前,你需要把三件事准备好:Rust 工具链、Wasm 编译目标,以及用于生成模板的 cargo-generate。
# 安装或更新 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 添加 Wasm 编译目标
rustup target add wasm32-unknown-unknown
# 安装 cargo-generate
cargo install cargo-generate
Wrangler 是 Cloudflare 的开发和部署工具。这里直接用 npx 调用即可,不强制全局安装:
npx wrangler --version
第一次执行 wrangler dev 或 wrangler deploy 时,Wrangler 会引导你登录 Cloudflare 账号。如果你是在没有浏览器 UI 的环境里操作,可以参考 Wrangler 登录文档走替代流程。
用官方模板创建项目
新项目直接从 workers-rs 官方模板开始:
cargo generate cloudflare/workers-rs
按提示选择模板。第一次练手推荐选 template/hello-world-http,项目名可以叫:
rust-kv-demo
生成后先看三个文件:
| 文件 | 你需要关心什么 |
|---|---|
Cargo.toml | Rust 依赖、crate 信息、Wasm release 优化配置 |
wrangler.toml 或 wrangler.jsonc | Worker 名称、构建命令、绑定和部署配置 |
src/lib.rs | Worker 的 Rust 入口 |
进入项目:
cd rust-kv-demo
模板会通过 worker-build 处理 Rust/Wasm 的打包流程。正常情况下,你不用自己补一层 JavaScript glue code,这也是官方模板省心的地方。
先确认 Hello World 能跑
模板里的 src/lib.rs 大概会长这样:
use worker::*;
#[event(fetch)]
async fn main(_req: Request, _env: Env, _ctx: Context) -> Result<Response> {
Response::ok("Hello, World!")
}
本地启动 Worker:
npx wrangler dev
打开 http://localhost:8787,能看到 Hello, World! 就说明这条链路已经打通了:
Rust 代码 → Wasm 构建 → Wrangler 本地运行 → Workers 请求处理
后面修改 src/lib.rs 时,Wrangler 会重新构建本地 Worker。第一次编译可能慢一点,后续体验会顺很多。
我们要做什么:一个 KV 小 API
为了让例子足够完整,但又不被业务细节淹没,这里做两个接口:
| 方法 | 路径 | 行为 |
|---|---|---|
POST | /:country | 请求体为 {"city": "Paris"},把国家和城市写入 KV |
GET | /:country | 从 KV 读取国家对应的城市 |
这个例子会顺手覆盖 workers-rs 的几个常用点:Request、Response、Env、Router、路径参数、JSON 解析,以及 Workers KV 绑定。
创建 KV Namespace
先创建一个名为 cities 的 KV namespace:
npx wrangler kv namespace create cities
命令执行后,Wrangler 会返回一段配置。把里面的 id 写进你的 Worker 配置文件。
如果项目使用 wrangler.toml:
[[kv_namespaces]]
binding = "cities"
id = "你的-namespace-id"
如果项目使用 wrangler.jsonc:
{
"kv_namespaces": [
{
"binding": "cities",
"id": "你的-namespace-id"
}
]
}
这里最容易踩坑的是 binding 名称。后面的 Rust 代码会用这个名字拿 KV:
ctx.kv("cities")?
所以配置里叫 cities,代码里也必须叫 cities。大小写、拼写都要一致。
添加 JSON 解析依赖
接口的 POST body 是 JSON,所以加上 serde:
cargo add serde --features derive
模板通常已经带了 worker 依赖。Cargo.toml 中大概会有类似内容:
[dependencies]
worker = "0.6"
serde = { version = "1", features = ["derive"] }
这里的 worker = "0.6" 只是示例。实际项目建议保留模板生成出来的版本,或者按 crates.io 当前稳定版本调整,不要把文章里的版本号当成必须照抄的固定值。
写完整的 src/lib.rs
把 src/lib.rs 改成下面这样:
use serde::{Deserialize, Serialize};
use worker::*;
#[event(fetch)]
async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
let router = Router::new();
#[derive(Serialize, Deserialize, Debug)]
struct Country {
city: String,
}
router
.post_async("/:country", |mut req, ctx| async move {
let country = ctx.param("country").unwrap();
let city = match req.json::<Country>().await {
Ok(c) => c.city,
Err(_) => String::from(""),
};
if city.is_empty() {
return Response::error("Bad Request", 400);
}
match ctx.kv("cities")?.put(country, &city)?.execute().await {
Ok(_) => Response::ok(city),
Err(_) => Response::error("Bad Request", 400),
}
})
.get_async("/:country", |_req, ctx| async move {
if let Some(country) = ctx.param("country") {
match ctx.kv("cities")?.get(country).text().await? {
Some(city) => Response::ok(city),
None => Response::error("Country not found", 404),
}
} else {
Response::error("Bad Request", 400)
}
})
.run(req, env)
.await
}
拆开看其实不复杂:
#[event(fetch)]声明这是 HTTP 请求入口。Router::new()用来组织不同路径和方法。ctx.param("country")读取/:country里的路径参数。ctx.kv("cities")通过绑定名获取 KV namespace。put(...).execute().await写入,get(...).text().await读取。
这段 demo 的错误处理刻意写得直白:JSON 解析失败或 city 为空就返回 400,找不到国家就返回 404。生产项目可以在这个基础上继续封装统一的 JSON 响应。
本地测试一下
启动开发服务器:
npx wrangler dev
写入一条数据:
curl --json '{"city": "Paris"}' http://localhost:8787/France
再读回来:
curl http://localhost:8787/France
如果返回:
Paris
说明路由、JSON 解析和 KV 绑定都已经连起来了。
如果这里报 KV 绑定错误,先别急着怀疑 Rust 代码,优先检查这三处:
wrangler.toml或wrangler.jsonc里是否真的写了kv_namespaces。binding是否正好叫cities。- 当前
wrangler dev读取的是否就是你刚刚编辑的配置文件。
部署到 Cloudflare
本地确认没问题后,直接部署:
npx wrangler deploy
部署成功后,可以访问你的 *.workers.dev 地址,也可以接入自定义域名。第一次启用 workers.dev 子域名时,DNS 生效可能会有一点延迟,等一会儿再测就好。
生产环境别忽略这些细节
控制 Wasm 体积
workers-rs 模板通常会在 release profile 里配置一些体积优化,比如 lto、strip 和 codegen-units。Cloudflare 的 Rust 文档也说明,worker-build 会参与 Wasm 打包和优化流程。
但这不代表可以随便加依赖。能不能编译到 wasm32-unknown-unknown、会不会带进一堆不必要的功能、最终包体是否可控,都是边缘应用需要提前考虑的问题。
crate 兼容性要提前确认
很多 Rust crate 可以在 Workers 上使用,但并不是所有 crate 都天然适配 Wasm。凡是依赖系统线程、本地文件系统、原生 TLS、阻塞网络调用的库,都要多看一眼。
Cloudflare 有 Supported crates 文档。像 serde、HTTP 客户端、时间处理这类常见依赖,也经常需要按 Wasm 场景开启正确 feature。
KV 只是入口,不是终点
这篇用 KV 是因为它最容易理解,也最适合做第一个 demo。实际项目里,workers-rs 还可以接入更多 Workers 绑定:
- R2:对象存储,适合文件、图片和静态资源。
- D1:SQLite 风格的关系型数据库。
- Durable Objects:适合有状态协调、房间模型、实时协作。
- Queues:适合异步任务和削峰处理。
- Workers AI:适合在边缘侧接入 AI 能力。
不同绑定的 Rust API、feature 和配置细节不完全一样。接入前建议先看 workers-rs 仓库和 Cloudflare 对应文档。
Demo 的错误处理不能直接搬去线上
为了让示例清楚,本文代码的错误处理很轻。真正对外提供 API 时,至少建议补上:
- 统一的 JSON 错误响应。
- 区分 JSON 格式错误、参数缺失、KV 读写失败和配置错误。
- 给
POSTbody 增加大小限制、字段长度限制和字符校验。 - 对公开接口增加鉴权、限流或来源校验。
边缘应用离用户近,也离异常流量近。API 边界从第一版就收紧,后面会少很多麻烦。
总结
用 workers-rs 写 Cloudflare Workers,核心流程可以压缩成六步:
- 准备 Rust、Wasm target、cargo-generate 和 Wrangler。
- 用
cargo generate cloudflare/workers-rs创建项目。 - 在
src/lib.rs里用#[event(fetch)]写请求入口。 - 用
Router组织 HTTP 路由。 - 在 Wrangler 配置里声明绑定,再通过
ctx.kv("...")等 API 访问。 - 用
npx wrangler dev本地调试,用npx wrangler deploy部署。
如果你的项目已经重度使用 Rust,workers-rs 是一条很自然的边缘计算路径。它不一定比 TypeScript Worker 更适合所有场景,但在类型约束、系统逻辑复用和 Wasm 部署这几件事上,确实有自己的优势。
参考资料
Mttao GitHub ↗
探索技术与生活的智慧