Cloudflare Workers+Pages+D1:构建无服务器边缘应用完整方案
本教程通过制作一个简单的“留言板”,学习 Cloudflare 三个核心产品:
- Cloudflare Pages:部署前端页面
- Cloudflare Workers:编写后端 API
- Cloudflare D1:保存留言数据
最终架构:
一、Cloudflare Pages、Workers、D1 分别是什么
| 组件 | 角色 | 可部署 / 运行内容 | 本项目承担功能 |
|---|---|---|---|
| Pages | 前端静态托管 | HTML、CSS、JS、Vue、React、静态网站 | 展示留言板标题、姓名输入框、留言输入框、提交按钮、留言列表 |
| Workers | 边缘后端 API 服务 | JavaScript / TypeScript,处理 HTTP 请求 | 接口:GET/api/helloGET/api/messagesPOST/api/messagesPOST/api/echo数据校验、读写 D1、返回 JSON、对接 KV/R2 |
| D1 | 边缘 SQLite 数据库 | SQLite 结构化数据存储(替代传统数据库) | 存储字段:留言编号、留言人姓名、留言内容、创建时间 |
二、准备 GitHub 仓库
本教程使用 GitHub 保存代码,并让 Cloudflare 自动部署。需要创建两个仓库:
my-worker:保存 Workers 后端代码my-pages:保存 Pages 前端代码
进入 https://github.com/new 两个仓库可以设置为 Public,也可以设置为 Private。
三、创建D1数据库
1、创建数据库
进入**【cloudflare主页】——【存储与数据库】——【D1 SQLite数据库】——【创建数据库】**
创建后记录你的Database name和Database UUID
2、创建数据表
进入 D1 数据库的控制台或 SQL 编辑器,执行:
CREATETABLEmessages(idINTEGERPRIMARYKEYAUTOINCREMENT,nameTEXTNOTNULL,messageTEXTNOTNULL,created_atTEXTNOTNULLDEFAULTCURRENT_TIMESTAMP);创建索引:
CREATEINDEXidx_messages_created_atONmessages(created_at);插入测试数据:
INSERTINTOmessages(name,message)VALUES('测试用户','这是第一条留言');查询测试:
SELECT*FROMmessagesORDERBYidDESC;如果可以查询到数据,说明表创建成功。
四、创建Worker项目
进入 Cloudflare 控制台:【计算】——【Workers 和 Pages】——【创建应用程序】——【Continue with GitHub】
授权 GitHub 后,选择:【my-worker】仓库,如果仓库为空,先在 GitHub 中添加以下文件。
my-worker/ ├── src/ │ └── index.js ├── wrangler.jsonc └── package.json代码示例:
wrangler.jsonc
{"name":"my-worker",//Worker 名称"main":"src/index.js",//入口文件"compatibility_date":"2026-08-26","d1_databases":[{"binding":"DB","database_name":"my-database",//填写上面的D1数据库名称"database_id":"22d7ad3f-cb4e-4aea-b47f-ae3f30674a28"//填写上面的D1数据库UUID}]}package.json
{"name":"my-worker","private":true,"version":"1.0.0","devDependencies":{"wrangler":"^4.26.0"}}index.js
constallowedOrigins=["https://app.xxxx.fun",//你的worker自定义域"https://e5fa97a5.my-pages-d7q.pages.dev"];functiongetCorsHeaders(request){constorigin=request.headers.get("Origin");constheaders={"Access-Control-Allow-Methods":"GET, POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type","Vary":"Origin"};if(origin&&allowedOrigins.includes(origin)){headers["Access-Control-Allow-Origin"]=origin;}returnheaders;}functionjsonResponse(request,data,status=200){returnResponse.json(data,{status,headers:getCorsHeaders(request)});}exportdefault{asyncfetch(request,env){consturl=newURL(request.url);constorigin=request.headers.get("Origin");// CORS 预检if(request.method==="OPTIONS"){if(origin&&!allowedOrigins.includes(origin)){returnnewResponse("CORS origin denied",{status:403});}returnnewResponse(null,{status:204,headers:getCorsHeaders(request)});}// 测试接口if(request.method==="GET"&&url.pathname==="/api/hello"){returnjsonResponse(request,{success:true,message:"Hello from Cloudflare Workers",time:newDate().toISOString()});}// 查询留言if(request.method==="GET"&&url.pathname==="/api/messages"){try{constresult=awaitenv.DB.prepare(`SELECT id, name, message, created_at FROM messages ORDER BY id DESC`).all();returnjsonResponse(request,{success:true,messages:result.results});}catch(error){returnjsonResponse(request,{success:false,message:"查询数据库失败",error:error.message},500);}}// 新增留言if(request.method==="POST"&&url.pathname==="/api/messages"){try{constdata=awaitrequest.json();constname=String(data.name||"").trim();constmessage=String(data.message||"").trim();if(!name||!message){returnjsonResponse(request,{success:false,message:"name 和 message 不能为空"},400);}if(name.length>50||message.length>1000){returnjsonResponse(request,{success:false,message:"内容长度超出限制"},400);}constresult=awaitenv.DB.prepare(`INSERT INTO messages (name, message) VALUES (?, ?)`).bind(name,message).run();returnjsonResponse(request,{success:true,id:result.meta.last_row_id},201);}catch(error){returnjsonResponse(request,{success:false,message:"写入数据库失败",error:error.message},500);}}// 原来的 echo 接口if(request.method==="POST"&&url.pathname==="/api/echo"){try{constdata=awaitrequest.json();returnjsonResponse(request,{success:true,received:data});}catch{returnjsonResponse(request,{success:false,message:"请求体必须是合法 JSON"},400);}}returnjsonResponse(request,{success:false,message:"Not Found"},404);}};在 Cloudflare 部署设置中填写:
| 配置项 | 内容 |
|---|---|
| 生产分支 | main |
| 根目录 | / |
| 构建命令 | 留空 |
| 部署命令 | npx wrangler deploy |
五、部署并测试 Worker
部署成功后,Cloudflare 会提供类似地址:
https://my-worker.你的账号.workers.dev后续也可以绑定自定义域名:【计算】——【Workers 和 Pages】——【my-worker】——【域】
这里我们添加了一个自定义域名
1、测试GET接口
curl-ihttps://api.xxxx.fun/api/hello2、测试POST接口
curl-i-XPOST https://api.xxxx.fun/api/echo\-H"Content-Type: application/json"\-d'{"name":"张三","message":"你好"}'六、绑定D1数据库到Worker
进入【Workers 和 Pages】——【my-worker】——【绑定】——【添加绑定】——选择【D1 database】
| 配置项 | 内容 |
|---|---|
| 变量名称 | DB,和wrangler.jsonc 中的binding:DB一致 |
| D1 数据库 | 选择my-database |
七、测试D1 API
#测试查询接口curl-ihttps://api.xxxx.fun/api/messages#测试写入接口curl-i-XPOST https://api.xxxx.fun/api/messages\-H"Content-Type: application/json"\-d'{"name":"张三","message":"这条数据保存到D1了"}'正常返回:200,成功查询和写入
八、创建 Pages 前端
在 GitHub 的my-pages仓库中创建:
index.html
<h2>留言板</h2><inputid="name"placeholder="你的名字"><br><br><textareaid="message"placeholder="留言内容"></textarea><br><br><buttononclick="submitMessage()">提交留言</button><buttononclick="loadMessages()">加载留言</button><preid="result">等待操作……</pre><script>constAPI_BASE="https://api.xxxx.fun";//你的pages自定义域asyncfunctionsubmitMessage(){constname=document.getElementById("name").value.trim();constmessage=document.getElementById("message").value.trim();if(!name||!message){alert("请填写姓名和留言");return;}constresponse=awaitfetch(`${API_BASE}/api/messages`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name,message})});constdata=awaitresponse.json();document.getElementById("result").textContent=JSON.stringify(data,null,2);if(response.ok){document.getElementById("name").value="";document.getElementById("message").value="";awaitloadMessages();}}asyncfunctionloadMessages(){constresponse=awaitfetch(`${API_BASE}/api/messages`);constdata=awaitresponse.json();document.getElementById("result").textContent=JSON.stringify(data,null,2);}loadMessages();</script>九、部署Pages
进入 Cloudflare:【Workers 和 Pages】——【创建应用程序】——【Pages】——【连接 Github】——【选择my-pages】
配置填写:
| 配置项 | 内容 |
|---|---|
| 生产分支 | main |
| 构建命令 | 留空 |
| 输出目录 | / |
| 根目录 | / |
部署成功后也同样绑定一个自定义域名:app.xxxx.fun。绑定后,前端地址为:https://app.xxxx.fun
十、最终测试
打开前端页面https://app.xxxx.fun
测试:
- 填写姓名;
- 填写留言;
- 点击“提交留言”;
- 页面请求 Workers;
- Workers 写入 D1;
- 页面重新查询并显示留言。
功能均正常
总结
本项目实现了从 GitHub 自动部署,到 Pages 前端、Workers API、D1 数据库存储的完整闭环,完成了一个可实际使用的 Cloudflare Serverless 留言板。