Appearance
实训:快速搭建 restful 后端
第 11 周的个人应用需要真正的后端,但此刻还没有学过后端开发——本实训的目标正是:不写(几行)后端代码,获得一个符合 REST 习惯、并进一步规范化为 JSON:API 形态的 API 服务,把精力留给前端。
两条路线:
- 路线 A:json-server——5 分钟起步,零代码,满足大部分练习需求
- 路线 B:30 行 express——亲手看清 JSON:API 信封是怎么包出来的
前置:Node 与 npm 已安装(见Node 环境构建;npm 基本命令见NPM)。工作目录:任意空文件夹 myapi/。
路线 A:json-server,5 分钟得到 REST API
- 写数据文件
db.json——它就是你的"数据库":
- 启动服务(无需写任何服务器代码):
bash
npx json-server db.json --port 3000- 用 REST 客户端验证五件套(工具见restful):
| 操作 | 请求 | 结果 |
|---|---|---|
| 列表 | GET http://localhost:3000/bookmarks | 全部书签 |
| 单个 | GET http://localhost:3000/bookmarks/1 | 一条书签 |
| 新增 | POST 同 URL,请求体 JSON | 自动生成 id 并入库 |
| 修改 | PUT .../bookmarks/1,请求体 JSON | 整条替换 |
| 删除 | DELETE .../bookmarks/1 | 从 db.json 移除 |
- 顺手体验查询能力(不写代码就有的):
plain
过滤与多值 GET /bookmarks?title=MDN
GET /posts?id=1&id=2
全文搜索 GET /bookmarks?q=css
比较谓词 GET /products?price_gte=10&price_lte=80 (_gte _lte _ne _like)
排序、分页 GET /products?_sort=price&_order=asc
GET /posts?_page=1&_limit=10
GET /posts?_start=20&_end=30&_limit=10
关联查询 GET /comments?author.name=张三打开 db.json 看——POST/PUT/DELETE 的结果真的写进了文件,这就是"持久化"。(Windows 下若用 --watch 无效的老版本,修改 db.json 后需重启服务。)
理解要点:json-server 把 db.json 的每个 key 自动映射为资源集合,资源的名词、动词的语义全部来自 REST 约定——你亲眼看到了规范带来的"零文档可用性"。
路线 B:30 行 express,看清 JSON:API 信封
json-server 返回的是"裸 JSON",不是 JSON:API 信封。想体会信封是怎么包出来的,手工写一遍最清楚。
- 初始化并安装依赖:
bash
npm init -y
npm install express- 写
server.js——内存数据库 + 统一信封:
javascript
const express = require("express");
const app = express();
app.use(express.json()); // 解析 JSON 请求体
let bookmarks = [ // 内存数据库
{ id: 1, title: "MDN", url: "https://developer.mozilla.org" },
];
// 信封函数:把裸数据包成 JSON:API 形态——所有接口共用
const wrap = (b) => ({
data: {
type: "bookmarks",
id: String(b.id),
attributes: { title: b.title, url: b.url },
},
});
app.get("/api/bookmarks", (req, res) =>
res.json({ data: bookmarks.map(wrap) }));
app.get("/api/bookmarks/:id", (req, res) => {
const b = bookmarks.find(x => x.id == req.params.id);
if (!b) return res.status(404).json({ errors: [{ title: "未找到" }] });
res.json(wrap(b));
});
app.post("/api/bookmarks", (req, res) => {
const b = { id: Date.now(), ...req.body };
bookmarks.push(b);
res.status(201).json(wrap(b)); // 201 Created
});
app.delete("/api/bookmarks/:id", (req, res) => {
bookmarks = bookmarks.filter(x => x.id != req.params.id);
res.status(204).end(); // 204 No Content
});
app.listen(3000, () => console.log("JSON:API on http://localhost:3000"));启动并对比:
node server.js,再用 REST 客户端请求http://localhost:3000/api/bookmarks。观察三个设计决定:
wrap()函数是全篇关键——信封与数据的分离只发生在一处,业务代码(增删查)完全不知道 JSON:API 的存在- 状态码开始"说话":201(已创建)、204(成功但无返回体)、404——它们是 API 语义的一部分(呼应HTTP协议)
- 错误用
errors数组返回——前端从此可以用一套逻辑处理所有错误
收尾:两条路线怎么选
| json-server | express 手写 | |
|---|---|---|
| 代码量 | 0 行 | ~30 行 |
| 返回形态 | 裸 JSON | 可控(示例为 JSON:API) |
| 适用 | 前端练习、原型 | 需要自定义行为时 |
- 第 11 周书签应用直接对接路线 A 即可;第 13 周 Node 之后回头看路线 B 会非常轻松
- 生产中的后端框架会自动产出 JSON:API(如 NestJS、Laravel 的 jsonapi 扩展),本实训的
wrap()就是它们的雏形 - 走得更远:Supabase 提供带登录等功能模块的托管 restful 服务,可以纯前端技术开发功能完善的联网应用(超出本周范围,仅作了解)
验收标准
- [ ] json-server 跑通 CRUD 五件套,POST 结果在 db.json 可见
- [ ] 能说出 REST 四条主张里的至少两条
- [ ] (选做)路线 B 跑通,并能指出 wrap() 在哪、201/204 各是什么含义