在本教程中,你将学习如何使用 Cloudflare Workers 平台 和 D1 数据库 在 Cloudflare Workers 上部署 Express.js ↗ 应用。你将构建一个成员注册表 API,具备基本的创建、读取、更新和删除(CRUD)操作。你将使用 D1 作为存储和检索成员数据的数据库。
所有教程都假设你已经完成了快速入门指南,该指南帮助你设置 Cloudflare Workers 账户、C3 ↗ 和 Wrangler。
如果想跳过步骤快速开始,请选择下方的 Deploy to Cloudflare(部署到 Cloudflare)。
这会在你的 GitHub 账户中创建仓库并将应用部署到 Cloudflare Workers。如果你熟悉 Cloudflare Workers 并希望跳过逐步指导,请使用此选项。
如果你是 Cloudflare Workers 新手,可能需要手动跟随步骤操作。
使用 C3(Cloudflare 开发者产品的命令行工具)创建新目录并初始化新的 Worker 项目:
npm create cloudflare@latest -- express-d1-appyarn create cloudflare express-d1-apppnpm create cloudflare@latest express-d1-app进行设置时,请选择以下选项:
- 对于 What would you like to start with?,选择
Hello World example。 - 对于 Which template would you like to use?,选择
Worker only。 - 对于 Which language do you want to use?,选择
TypeScript。 - 对于 Do you want to use git for version control?,选择
Yes。 - 对于 Do you want to deploy your application?,选择
No(部署前我们还会做一些修改)。
进入新项目目录:
cd express-d1-app在本教程中,你将使用 Express.js ↗,这是 Node.js 的流行 Web 框架。要在 Cloudflare Workers 环境中使用 Express,请安装 Express 以及必要的 TypeScript 类型:
npm i express @types/expressyarn add express @types/expresspnpm add express @types/expressbun add express @types/expressCloudflare Workers 上的 Express.js 需要 nodejs_compat 兼容性标志。此标志启用 Node.js API,允许 Express 在 Workers 运行时上运行。将以下内容添加到 Wrangler 配置文件:
{
"compatibility_flags": [
"nodejs_compat"
]
}compatibility_flags = [ "nodejs_compat" ]现在你将创建 D1 数据库来存储成员信息。使用 wrangler d1 create 命令创建新数据库:
npx wrangler d1 create members-db该命令将创建新的 D1 数据库并询问以下问题:
- Would you like Wrangler to add it on your behalf?:输入
Y。 - What binding name would you like to use?:输入
DB并按 Enter。 - For local dev, do you want to connect to the remote resource instead of a local resource?:输入
N。
⛅️ wrangler 4.44.0
───────────────────
✅ Successfully created DB 'members-db' in region WNAM
Created your new D1 database.
To access your new D1 Database in your Worker, add the following snippet to your configuration file:
{
"d1_databases": [
{
"binding": "members_db",
"database_name": "members-db",
"database_id": "<unique-ID-for-your-database>"
}
]
}
✔ Would you like Wrangler to add it on your behalf? … yes
✔ What binding name would you like to use? … DB
✔ For local dev, do you want to connect to the remote resource instead of a local resource? … no绑定将添加到你的 Wrangler 配置文件。
{
"d1_databases": [
{
"binding": "DB",
"database_name": "members-db",
"database_id": "<unique-ID-for-your-database>"
}
]
}[[d1_databases]]
binding = "DB"
database_name = "members-db"
database_id = "<unique-ID-for-your-database>"在项目根目录创建名为 schemas 的目录,在其中创建名为 schema.sql 的文件:
DROP TABLE IF EXISTS members;
CREATE TABLE IF NOT EXISTS members (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
joined_date TEXT NOT NULL
);
-- Insert sample data
INSERT INTO members (name, email, joined_date) VALUES
('Alice Johnson', 'alice@example.com', '2024-01-15'),
('Bob Smith', 'bob@example.com', '2024-02-20'),
('Carol Williams', 'carol@example.com', '2024-03-10');此架构创建 members 表,包含自增 ID、姓名、电子邮件和加入日期字段。它还插入三名示例成员。
对 D1 数据库执行架构文件:
npx wrangler d1 execute members-db --file=./schemas/schema.sql上述命令在本地开发数据库中创建表。稍后你将把架构部署到生产环境。
更新 src/index.ts 文件以设置带 TypeScript 的 Express。用以下内容替换文件内容:
import { env } from "cloudflare:workers";
import { httpServerHandler } from "cloudflare:node";
import express from "express";
const app = express();
// Middleware to parse JSON bodies
app.use(express.json());
// Health check endpoint
app.get("/", (req, res) => {
res.json({ message: "Express.js running on Cloudflare Workers!" });
});
app.listen(3000);
export default httpServerHandler({ port: 3000 });此代码初始化 Express 并创建基本的健康检查端点。关键导入 import { env } from "cloudflare:workers" 允许你从代码中的任何位置访问 绑定(binding),如 D1 数据库。httpServerHandler 将 Express 与 Workers 运行时集成,使应用能够在 Cloudflare 网络上处理 HTTP 请求。
接下来,执行 typegen 命令为 Worker 环境生成类型定义:
npm run cf-typegen添加从数据库检索成员的端点。在健康检查端点之后,向 src/index.ts 文件添加以下路由:
// GET all members
app.get('/api/members', async (req, res) => {
try {
const { results } = await env.DB.prepare('SELECT * FROM members ORDER BY joined_date DESC').all();
res.json({ success: true, members: results });
} catch (error) {
res.status(500).json({ success: false, error: 'Failed to fetch members' });
}
});
// GET a single member by ID
app.get('/api/members/:id', async (req, res) => {
try {
const { id } = req.params;
const { results } = await env.DB.prepare('SELECT * FROM members WHERE id = ?').bind(id).all();
if (results.length === 0) {
return res.status(404).json({ success: false, error: 'Member not found' });
}
res.json({ success: true, member: results[0] });
} catch (error) {
res.status(500).json({ success: false, error: 'Failed to fetch member' });
}
});这些路由使用 D1 绑定(env.DB)准备 SQL 语句并执行。由于你在文件顶部从 cloudflare:workers 导入了 env,它可在整个应用中访问。D1 绑定上的 prepare、bind 和 all 方法允许你安全地查询数据库。有关所有可用方法,请参阅 D1 Workers Binding API。
添加创建新成员的端点。向 src/index.ts 文件添加以下路由:
// POST - Create a new member
app.post("/api/members", async (req, res) => {
try {
const { name, email } = req.body;
// Validate input
if (!name || !email) {
return res.status(400).json({
success: false,
error: "Name and email are required",
});
}
// Basic email validation (simplified for tutorial purposes)
// For production, consider using a validation library or more comprehensive checks
if (!email.includes("@") || !email.includes(".")) {
return res.status(400).json({
success: false,
error: "Invalid email format",
});
}
const joined_date = new Date().toISOString().split("T")[0];
const result = await env.DB.prepare(
"INSERT INTO members (name, email, joined_date) VALUES (?, ?, ?)"
)
.bind(name, email, joined_date)
.run();
if (result.success) {
res.status(201).json({
success: true,
message: "Member created successfully",
id: result.meta.last_row_id,
});
} else {
res
.status(500)
.json({ success: false, error: "Failed to create member" });
}
} catch (error: any) {
// Handle unique constraint violation
if (error.message?.includes("UNIQUE constraint failed")) {
return res.status(409).json({
success: false,
error: "Email already exists",
});
}
res.status(500).json({ success: false, error: "Failed to create member" });
}
});此端点验证输入、检查电子邮件格式,并将新成员插入数据库。它还通过检查唯一约束违规来处理重复电子邮件地址。
添加更新现有成员的端点。向 src/index.ts 文件添加以下路由:
app.put("/api/members/:id", async (req, res) => {
try {
const { id } = req.params;
const { name, email } = req.body;
// Validate input
if (!name && !email) {
return res.status(400).json({
success: false,
error: "At least one field (name or email) is required",
});
}
// Basic email validation if provided (simplified for tutorial purposes)
// For production, consider using a validation library or more comprehensive checks
if (email && (!email.includes("@") || !email.includes("."))) {
return res.status(400).json({
success: false,
error: "Invalid email format",
});
}
// Build dynamic update query
const updates: string[] = [];
const values: any[] = [];
if (name) {
updates.push("name = ?");
values.push(name);
}
if (email) {
updates.push("email = ?");
values.push(email);
}
values.push(id);
const result = await env.DB.prepare(
`UPDATE members SET ${updates.join(", ")} WHERE id = ?`
)
.bind(...values)
.run();
if (result.meta.changes === 0) {
return res
.status(404)
.json({ success: false, error: "Member not found" });
}
res.json({ success: true, message: "Member updated successfully" });
} catch (error: any) {
if (error.message?.includes("UNIQUE constraint failed")) {
return res.status(409).json({
success: false,
error: "Email already exists",
});
}
res.status(500).json({ success: false, error: "Failed to update member" });
}
});此端点允许更新现有成员的姓名、电子邮件或两者。它根据提供的字段构建动态 SQL 查询。
添加删除成员的端点。向 src/index.ts 文件添加以下路由:
// DELETE - Delete a member
app.delete("/api/members/:id", async (req, res) => {
try {
const { id } = req.params;
const result = await env.DB.prepare("DELETE FROM members WHERE id = ?")
.bind(id)
.run();
if (result.meta.changes === 0) {
return res
.status(404)
.json({ success: false, error: "Member not found" });
}
res.json({ success: true, message: "Member deleted successfully" });
} catch (error) {
res.status(500).json({ success: false, error: "Failed to delete member" });
}
});此端点按 ID 删除成员,如果成员不存在则返回错误。
启动开发服务器以在本地测试 API:
npm run dev开发服务器将启动,你可以在 http://localhost:8787 访问 API。
打开新的终端窗口并使用 curl 测试端点:
curl http://localhost:8787/api/members{
"success": true,
"members": [
{
"id": 1,
"name": "Alice Johnson",
"email": "alice@example.com",
"joined_date": "2024-01-15"
},
{
"id": 2,
"name": "Bob Smith",
"email": "bob@example.com",
"joined_date": "2024-02-20"
},
{
"id": 3,
"name": "Carol Williams",
"email": "carol@example.com",
"joined_date": "2024-03-10"
}
]
}测试创建新成员:
curl -X POST http://localhost:8787/api/members \
-H "Content-Type: application/json" \
-d '{"name": "David Brown", "email": "david@example.com"}'{
"success": true,
"message": "Member created successfully",
"id": 4
}测试获取单个成员:
curl http://localhost:8787/api/members/1测试更新成员:
curl -X PUT http://localhost:8787/api/members/1 \
-H "Content-Type: application/json" \
-d '{"name": "Alice Cooper"}'测试删除成员:
curl -X DELETE http://localhost:8787/api/members/4部署到生产环境之前,对远程(生产)数据库执行架构文件:
npx wrangler d1 execute members-db --remote --file=./schemas/schema.sql现在将应用部署到 Cloudflare 网络:
npm run deploy⛅️ wrangler 4.44.0
───────────────────
Total Upload: 1743.64 KiB / gzip: 498.65 KiB
Worker Startup Time: 48 ms
Your Worker has access to the following bindings:
Binding Resource
env.DB (members-db) D1 Database
Uploaded express-d1-app (2.99 sec)
Deployed express-d1-app triggers (5.26 sec)
https://<your-subdomain>.workers.dev
Current Version ID: <version-id>部署成功后,Wrangler 将输出 Worker 的 URL。
使用提供的 URL 测试已部署的 API。将 <your-worker-url> 替换为实际的 Worker URL:
curl https://<your-worker-url>/api/members你应该看到在生产数据库中创建的相同成员数据。
在生产环境中创建新成员:
curl -X POST https://<your-worker-url>/api/members \
-H "Content-Type: application/json" \
-d '{"name": "Eva Martinez", "email": "eva@example.com"}'你的 Express.js 应用与 D1 数据库现已在 Cloudflare Workers 上运行。
在本教程中,你使用 Express.js 和 D1 数据库构建了成员注册表 API,并将其部署到 Cloudflare Workers。你实现了完整的 CRUD 操作(创建、读取、更新、删除),并学会了如何:
- 为 Cloudflare Workers 设置 Express.js 应用
- 创建并配置带绑定的 D1 数据库
- 使用 D1 预编译语句实现数据库操作
- 在本地和生产环境中测试 API
- 了解更多 D1 数据库功能
- 探索 Workers 路由和中间件
- 使用 Workers 身份验证 为 API 添加身份验证
- 使用 D1 查询优化 为大型数据集实现分页