使用 Wrangler CLI 构建与部署 Cloudflare API、D1 数据库与 KV 缓存
手把手教你使用 Cloudflare Workers、Wrangler CLI、D1 关系型数据库和 KV 缓存构建高性能 Serverless API 并实现全球部署。
在传统的云架构中,构建一个低延迟的全球分布式 API 往往意味着要在多个区域部署容器、配置负载均衡器以及维护繁重的数据库只读副本(Read Replicas)。
借助 Cloudflare 边缘计算平台与 Wrangler CLI,开发者可以在几分钟内快速构建、本地调试并部署一套结合了关系型数据库(D1)与高速键值缓存(KV)的全栈 REST API — 瞬时运行于全球 300 多个数据中心,实现零冷启动(Zero Cold Start)。
1. 架构核心概念(通俗易懂版)#
我们可以把这套系统比作一家高档餐厅:
- Cloudflare Worker(服务员 / 厨师):在离顾客最近的分店(边缘节点)接收点单并执行业务逻辑。
- Cloudflare KV(熟食展示保温柜 / 高速缓存):存放预制好的热门菜品。当有顾客点单时,服务员无需进入后厨即可秒级出餐(读取延迟低于 1ms)。
- Cloudflare D1(中央食材库与账本 / SQL 数据库):基于 SQLite 的 Serverless 关系型数据库,负责可靠存储全部底层业务数据并执行精准的 SQL 查询。
graph TD
Client["📱 客户端请求 (Client Request)"] -->|HTTP GET/POST| Worker["⚡ Cloudflare Worker (Edge API)"]
Worker -->|1. 检查缓存| KV["⚡ Cloudflare KV (超高速读缓存)"]
KV -.->|Cache Hit - 命中直接返回| Worker
KV -.->|Cache Miss| D1
Worker -->|2. 查询/修改持久化数据| D1["🗄️ Cloudflare D1 (Serverless SQLite)"]
D1 -->|3. 回填缓存| KV
Worker -->|HTTP JSON Response| Client
2. 环境准备与项目初始化#
请确保已安装 Node.js ↗ 或 Bun ↗,并准备好 Cloudflare 账号。
步骤 1: 创建 Worker 项目#
使用官方脚手架创建 TypeScript 模板项目:
# 初始化 TypeScript 项目
npm create cloudflare@latest edge-api -- --type=hello-world-typescript --ts --git --deploy=false
cd edge-apibash安装最新版 Wrangler CLI 工具:
npm install -D wranglerbash登录你的 Cloudflare 账户:
npx wrangler loginbash3. 配置 Cloudflare D1(SQL 数据库)#
Cloudflare D1 是一个运行在边缘的分布式 Serverless SQLite 数据库。
步骤 1: 创建 D1 数据库#
运行以下命令创建名为 ecommerce-db 的数据库:
npx wrangler d1 create ecommerce-dbbash命令完成后将输出绑定配置信息:
[[d1_databases]]
binding = "DB"
database_name = "ecommerce-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"text步骤 2: 编写数据库 Schema 并执行迁移#
在项目根目录下创建 schema.sql:
DROP TABLE IF EXISTS products;
CREATE TABLE products (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
price REAL NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO products (id, name, price) VALUES
('prod_1', '无线机械键盘', 129.99),
('prod_2', '带鱼屏电竞显示器', 499.99),
('prod_3', '人体工学升降桌', 349.50);sql在本地开发环境执行 Schema:
npx wrangler d1 execute ecommerce-db --local --file=./schema.sqlbash在云端生产环境执行 Schema:
npx wrangler d1 execute ecommerce-db --remote --file=./schema.sqlbash4. 配置 Cloudflare KV(键值缓存)#
Cloudflare KV 是专为高频读取、低延迟场景打造的全球分布式键值存储系统。
步骤 1: 创建 KV 命名空间#
分别创建生产环境与测试环境的 Namespace:
# 创建生产环境命名空间
npx wrangler kv namespace create CACHE_KV
# 创建预览/测试环境命名空间
npx wrangler kv namespace create CACHE_KV --previewbash控制台将输出对应的 ID 配置:
[[kv_namespaces]]
binding = "CACHE_KV"
id = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
preview_id = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"text5. 配置 wrangler.toml 文件#
在 wrangler.toml 中配置 D1 和 KV 的绑定关系:
name = "edge-api"
main = "src/index.ts"
compatibility_date = "2026-08-01"
# 绑定 D1 数据库
[[d1_databases]]
binding = "DB"
database_name = "ecommerce-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# 绑定 KV 缓存
[[kv_namespaces]]
binding = "CACHE_KV"
id = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
preview_id = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"toml自动生成 TypeScript 绑定类型声明:
npx wrangler typesbash6. 使用 Cache-Aside 模式实现 API#
在 src/index.ts 中编写业务代码,应用经典的 旁路缓存(Cache-Aside) 模式:
- 收到
GET /api/products/:id请求时,首先从 KV 查找缓存。 - 缓存命中(HIT):直接返回缓存结果。
- 缓存未命中(MISS):查询 D1 数据库,将结果写入 KV(设置 TTL 过期时间),并返回给客户端。
- 收到
POST /api/products请求时:向 D1 写入新记录,并同步更新 KV 缓存。
export interface Env {
DB: D1Database;
CACHE_KV: KVNamespace;
}
interface Product {
id: string;
name: string;
price: number;
created_at?: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
const path = url.pathname;
const method = request.method;
// 路由: GET /api/products/:id
if (method === 'GET' && path.startsWith('/api/products/')) {
const id = path.replace('/api/products/', '');
const cacheKey = `product:${id}`;
// 1. 读取 KV 缓存
const cached = await env.CACHE_KV.get(cacheKey, 'json');
if (cached) {
return Response.json(
{ source: 'kv-cache', data: cached },
{ headers: { 'X-Cache-Status': 'HIT', 'Content-Type': 'application/json' } }
);
}
// 2. 缓存未命中: 从 D1 数据库查询
const product = await env.DB.prepare('SELECT * FROM products WHERE id = ?')
.bind(id)
.first<Product>();
if (!product) {
return Response.json({ error: '未找到该商品' }, { status: 404 });
}
// 3. 异步写入 KV 缓存(设置 60 秒过期)
ctx.waitUntil(
env.CACHE_KV.put(cacheKey, JSON.stringify(product), {
expirationTtl: 60,
})
);
return Response.json(
{ source: 'd1-database', data: product },
{ headers: { 'X-Cache-Status': 'MISS', 'Content-Type': 'application/json' } }
);
}
// 路由: POST /api/products
if (method === 'POST' && path === '/api/products') {
const body = (await request.json()) as Partial<Product>;
if (!body.name || !body.price) {
return Response.json({ error: '缺少商品名称或价格' }, { status: 400 });
}
const id = `prod_${Date.now()}`;
await env.DB.prepare('INSERT INTO products (id, name, price) VALUES (?, ?, ?)')
.bind(id, body.name, body.price)
.run();
const newProduct: Product = { id, name: body.name, price: body.price };
// 更新 KV 缓存
await env.CACHE_KV.put(`product:${id}`, JSON.stringify(newProduct), {
expirationTtl: 300,
});
return Response.json({ success: true, product: newProduct }, { status: 201 });
}
return new Response('Edge API 正在稳定运行中!', { status: 200 });
},
};typescript[!TIP] 善用
ctx.waitUntil(...)执行缓存写入,可确保客户端第一时间获取响应,而不必等待后台的异步写入操作。
7. 本地调试与一键生产部署#
本地环境调试#
在本地启动带有 D1 和 KV 模拟环境的开发服务器:
npx wrangler devbash使用 curl 验证接口:
# 第一次请求 (MISS - 从 D1 查询)
curl http://localhost:8787/api/products/prod_1
# 第二次请求 (HIT - 从 KV 缓存直接返回)
curl http://localhost:8787/api/products/prod_1bash全球一键部署#
运行部署命令,将代码发布到 Cloudflare 全球边缘网络:
npx wrangler deploybash部署完成后,你将获得一个可立即访问的公网边缘地址,如 https://edge-api.<subdomain>.workers.dev!
8. 使用 Rust 与 Axum 开发有何不同?#
如果你更青睐 Rust 带来的内存安全与极致性能,Cloudflare Workers 原生支持将 Rust 编译为 WebAssembly(wasm32-unknown-unknown),并通过官方 worker-rs ↗ 库将 Axum ↗ 框架无缝运行在边缘端。
架构对比:TypeScript vs Rust (Axum)#
graph LR
subgraph TS["TypeScript Worker"]
direction TB
TS_Code["src/index.ts"] --> V8["V8 Isolate 引擎<br/>(原生 JS 绑定)"]
V8 --> DB_TS["env.DB / env.CACHE_KV"]
end
subgraph RS["Rust + Axum Worker"]
direction TB
RS_Code["src/lib.rs (Axum Router)"] --> WASM["WASM 模块<br/>(wasm32-unknown-unknown)"]
WASM --> WRS["worker-rs FFI 桥接"]
WRS --> DB_RS["env.d1('DB') / env.kv('CACHE_KV')"]
end
| 维度 | TypeScript | Rust (Axum + worker-rs) |
|---|---|---|
| 编译产物 | JavaScript(直接在 V8 Isolate 运行) | WebAssembly(.wasm,通过 worker-build 构建) |
| 路由模式 | 标准 fetch(request, env, ctx) 入口 | axum::Router 路由树与 Extractor(State、Path、Json) |
| 绑定访问 | 原生注入对象:env.DB、env.CACHE_KV | worker::Env FFI 提取:env.d1("DB")?、env.kv("CACHE_KV")? |
| 性能与安全 | 快速迭代、开发体验优秀 | 零成本抽象、编译期强类型安全、无运行时 GC 压力 |
wrangler.toml 配置 | main = "src/index.ts" | main = "build/worker/shim.mjs" 并配置 [build] command = "worker-build --release" |
Rust Axum 代码实战(集成 D1 与 KV)#
在 Rust 中,我们在 Worker 的 fetch 事件入口中初始化 axum::Router,并通过 Axum 的 State 共享 worker::Env 运行时环境:
use axum::{
extract::{Path, State},
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tower_service::Service;
use worker::*;
#[derive(Clone)]
struct AppState {
env: Arc<worker::Env>,
}
#[derive(Serialize, Deserialize)]
struct Product {
id: String,
name: String,
price: f64,
}
// GET /api/products/:id (KV 旁路缓存优先 & D1 兜底查询)
async fn get_product(
Path(id): Path<String>,
State(state): State<AppState>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let kv = state.env.kv("CACHE_KV").map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let cache_key = format!("product:{}", id);
// 1. 优先读取 KV 缓存
if let Ok(Some(cached_json)) = kv.get(&cache_key).text().await {
return Ok((
StatusCode::OK,
[("X-Cache-Status", "HIT"), ("Content-Type", "application/json")],
cached_json,
));
}
// 2. 缓存未命中: 查询 D1 关系型数据库
let d1 = state.env.d1("DB").map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let statement = d1.prepare("SELECT id, name, price FROM products WHERE id = ?1").bind(&[&id.into()])
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let product = statement.first::<Product>(None).await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
.ok_or_else(|| (StatusCode::NOT_FOUND, "未找到该商品".to_string()))?;
let json_str = serde_json::to_string(&product)
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// 3. 写入 KV 缓存(设置 60 秒 TTL)
let _ = kv.put(&cache_key, &json_str).unwrap().expiration_ttl(60).execute().await;
Ok((
StatusCode::OK,
[("X-Cache-Status", "MISS"), ("Content-Type", "application/json")],
json_str,
))
}
#[event(fetch)]
async fn fetch(req: HttpRequest, env: Env, _ctx: Context) -> Result<axum::http::Response<axum::body::Body>> {
let state = AppState { env: Arc::new(env) };
let mut router = Router::new()
.route("/api/products/:id", get(get_product))
.with_state(state);
let response = router.call(req).await.unwrap();
Ok(response)
}rust9. Wrangler CLI 常用命令速查表#
| 命令 | 功能说明 |
|---|---|
wrangler login | 登录授权 Cloudflare 账户 |
wrangler dev | 启动本地全功能模拟开发环境 |
wrangler d1 create <name> | 创建全新的 D1 SQL 数据库 |
wrangler d1 execute <name> --file=./schema.sql | 执行 SQL 脚本文件(--local 本地或 --remote 远端) |
wrangler kv namespace create <name> | 创建新的 KV 键值命名空间 |
wrangler types | 自动生成 Worker 绑定的 TypeScript 类型定义 |
wrangler deploy | 构建并将 Worker 发布到全球生产环境 |
wrangler tail | 在终端实时查看生产环境的实时日志 |