LabHub
学习 学习路径 课程

Node.js 后端 — 框架藏起来的东西

用 Node 标准库把 API 立起来

在 LabHub 中继续学习

目标

不使用 Nest,只用 Node 标准库构建一个小型订单 API。八个步骤依次涉及路由表、 输入验证、输出结构、依赖注入和事件循环。

为什么这样做

实验 Pod 只开放 DNS,无法执行 npm install。但这个限制反而很适合本课程—— 亲自做出原本由框架代替你做的判断,今后阅读 Nest 时,就能看出每个机制 替代了什么工作

格式

创建 /root/work/api/app.mjs,并 export 以下内容。

不打开 socket,是为了让评分不受端口冲突或启动时序影响。只有失败确实代表 真实失败,测试才有价值。

搭起服务器骨架

执行 mkdir -p /root/work/api 后,在 app.mjs 中定义 export async function handle(req)。req 是 {method, path, body, headers},返回值是 {status, body}/healthz 应返回 200 和 {status:'ok'}

handle('/healthz') 返回 200 和 {status:'ok'}

创建路由表——区分 404 与 405

把路径列表放在数组中,先筛出路径匹配的项,再检查 method。没有路径时返回 404;路径存在但 method 不支持时返回 405。GET /items 返回一个数组。

不存在的路径返回 404;已存在路径上的其他 method 返回 405

验证输入并说明错在哪里

POST /items 接收 {name: string, qty: number}。输入错误时返回 400 和 {errors:[...]},明确写出哪个字段为什么错误;正常时返回 201。

qty 为字符串时返回 400,errors 中含 qty;正常时返回 201

区分存储结构与输出结构

存储中有 secret,但响应中不能有。不要在每个 handler 中分别删除,而要在同一个地方选择允许输出的字段——这就是 allowlist。

GET /items 响应中没有 secret,但包含 id、name 和 qty

不存在的资源应返回 404

实现 GET /items/:id。找不到时返回 404。如果用 200 搭配 {error:...},客户端会把它当作成功——状态码本身就是契约。

不存在的 id 返回 404;存在的 id 返回 200 和对应项

通过注入接收存储

创建 export function createApp({store}),让 handler 只使用传入的 store。直接引用全局变量,就无法在下一步替换它。

createApp({store}) 中注入的存储被实际使用,输出结构也保持不变

不要阻塞事件循环

实现 GET /slow,使它至少耗时 40ms。若用 while 消耗时间,计时器也会一起停住。用 await new Promise(r => setTimeout(r, 50)) 主动让出执行权。

同时处理四个请求期间,5ms 计时器仍能继续运行

不启动服务器也能测试契约

app.test.mjs 中编写至少两个可由 node --test 运行的测试。通过 createApp 注入假存储进行测试——不得打开端口。

node --test 至少通过 2 个测试,并且没有启动服务器