前端与后端 / 技术笔记

Node.js 与 API 设计

从一次 HTTP 请求开始,理解接口约定、代码分层以及服务端如何稳定地返回结果。

内容章节
6 节
阅读重点
契约与分层
示例语言
TypeScript

前端调用接口时,看起来只是发送一个地址:

TypeScript 示例

typescript

const response = await fetch("/api/projects/42");

但服务端要完成路由匹配、参数校验、业务判断、数据库查询和错误处理。API 设计的目标,就是让这些步骤既能合作,又不会混成一团。

API 是前端和服务端共同遵守的完整约定,网址只是其中一部分。

1. API 是前端和服务端的约定

一个清楚的接口会说明请求方法、路径、输入、输出和可能失败的方式。

文本示意

text

GET /api/projects/42
成功:200 + 项目数据不存在:404 + 错误说明未登录:401 + 登录提示

如果返回格式每天都变,前端就只能不断增加特殊判断。稳定的契约能让两边独立开发,也让自动测试有明确目标。

2. 一次请求经过了什么

以 Express 为例,请求通常会经过一条管道:

文本示意

text

浏览器日志与鉴权中间件路由控制器业务服务数据库

中间件适合处理许多请求都需要的事情,例如记录耗时、解析 JSON 和验证登录身份。

TypeScript 示例

typescript

app.use(express.json());app.use(requestLogger);app.use("/api/projects", projectRouter);

3. 路由、控制器和服务怎样分工

路由回答“这个地址交给谁”,控制器负责读取请求和返回响应,服务层负责业务规则。

TypeScript 示例

typescript

router.get("/:id", projectController.getById);
async function getById(req: Request, res: Response) {    const project = await projectService.findById(req.params.id);    res.json(project);}

服务层与 reqres 保持解耦。这样它既能被 HTTP 接口调用,也能被定时任务和测试调用。

4. 参数校验为什么要在入口完成

服务端不能假设收到的数据一定正确。空标题、负数价格和格式错误的邮箱,都应该在进入核心业务前被拒绝。

TypeScript 示例

typescript

const input = createProjectSchema.parse(req.body);const project = await projectService.create(input);

入口校验像门卫。它不能保证业务一定成功,但能阻止明显错误的数据继续向下流动。

校验失败应该明确指出有问题的字段和原因,笼统的“系统异常”无法帮助调用方修正请求。

5. 错误处理和状态码如何配合

错误也属于 API 契约的一部分。常见状态可以这样理解:

文本示意

text

400  请求内容不符合要求401  还没有登录403  已登录但没有权限404  目标资源不存在409  当前状态发生冲突500  服务端出现未预期错误

集中错误处理中间件可以把业务异常统一转换成响应,避免每个控制器都写一遍 try/catch

TypeScript 示例

typescript

app.use((error, req, res, next) => {    const result = toApiError(error);    res.status(result.status).json(result.body);});

6. 一个可维护 API 的完整结构

一次创建项目的请求,可以按下面的顺序运行:

文本示意

text

POST /api/projects中间件确认登录身份校验请求字段控制器调用 projectService服务层执行权限和业务判断Repository 写入数据库控制器返回 201

排错时也可以反向检查这条链路。先确认请求有没有到达,再依次查看校验、业务和数据库,最后进入对应文件定位代码。