写出第一个后端接口
后端接收请求、执行可信逻辑并返回结构化响应。框架负责路由和协议细节,业务逻辑仍需要人来定义。
- 可以从浏览器或命令行调用自己写的 API。
- 路由、处理函数、参数和响应模型
- 实现 health 接口与一个接收文本、返回统计结果的 POST 接口。
- 完成 5.2「后端运行环境与依赖隔离」
FOUNDATION
必须理解
后端接收请求、执行可信逻辑并返回结构化响应。框架负责路由和协议细节,业务逻辑仍需要人来定义。
后端在可信环境接收请求、验证输入、执行规则并返回响应。第一个接口的重点不是框架语法,而是看清路由、处理函数和响应之间的关系。
前端是柜台,后端处理函数是办事窗口。窗口先确认你要办什么、材料是否齐全,再按规则处理并给回执;不能因为柜台说“合法”就跳过检查。
在 Next.js App Router 中创建 /api/tasks Route Handler,GET 返回示例任务,POST 读取 JSON 标题并返回新任务。
路由、处理函数、参数和响应模型
路由会把某个 URL 和方法交给对应的处理函数。Next.js 的 Route Handler 放在 app/api/.../route.ts,同一个地址可以分别处理 GET 和 POST。
基础概念
路由把 HTTP 方法与 URL 匹配到处理函数;参数把请求中的路径、查询或正文数据交给函数;响应模型规定返回状态、头部和正文的结构。
进一步理解
同一个 `/api/tasks` 可以用 GET 读取、POST 创建。框架完成匹配和基础解析,处理函数仍要校验输入、执行规则并选择正确响应。
路径参数常标识资源,查询参数常描述筛选,正文承载复杂提交。来源不同不代表可信度不同,全部外部输入都要验证。
POST `/api/tasks` 进入创建函数,读取 JSON 标题,校验后生成任务,返回 201 和任务对象。
路由文件不是数据库表,响应模型也不是内部实体;入口契约应与存储实现保持可控距离。
这一小节记住:一个后端端点就是明确入口、可信处理和结构化响应。
前端代码与服务端代码的信任边界
请求里有方法、请求头和正文,响应里有状态码、响应头和正文。服务端可以读取秘密配置,但从浏览器传来的每一个值都得当成不可信输入。
基础概念
信任边界是数据从不受控制环境进入可信执行环境的位置。浏览器、URL、请求头和 JSON 都可被调用者修改,服务端必须重新验证身份、权限和业务规则。
进一步理解
前端校验改善体验,可以提前提示空标题;攻击者或其他程序却能绕过页面直接发请求,因此不能把前端结果当安全依据。
服务端能访问数据库和秘密配置,返回响应时要只挑选允许公开字段。日志与错误也不能泄露堆栈、路径和凭证。
即使按钮只给管理员显示,POST 接口仍从服务端会话读取真实身份并检查角色,而不相信请求体中的 `role:'admin'`。
TypeScript 类型只在开发和编译阶段提供检查,网络传来的 JSON 在运行时仍可能是任意形状。
这一小节记住:凡是跨过网络进入服务端的数据,都先当成不可信输入。
开发服务器、端口和接口调试
TypeScript 能在写代码时提醒你类型不对,却管不到网络上真正发来的 JSON。别人完全可以绕过你的页面直接调用接口,所以运行时还要再校验。
基础概念
开发服务器是在本机监听端口、提供快速调试能力的进程。接口调试是用浏览器 Network、命令行或客户端工具检查请求和响应,而不仅看页面提示。
进一步理解
服务启动后会打印绑定地址与端口。连接失败先确认进程仍在运行、地址正确、端口未被占用;接口错误再看状态码、正文和服务端日志。
日志应带时间、请求标识和必要上下文,不能记录秘密。一次调试只改变一个变量,修改后重发同一请求比较结果。
用开发工具向 localhost 上的 POST 接口发送一个合法标题和一个空标题,对比 201 与 400,并在服务端日志找到两次请求。
浏览器能打开首页不代表 API 端口和路径正确;同一应用也可能有页面路由与接口路由两套处理。
这一小节记住:调试后端要同时观察客户端响应与服务端处理证据。
- 代码位于 Next.js App Router 的 `app/api/tasks/route.ts`。
- 示例只演示输入校验与响应,尚未写入数据库或检查用户身份。
export async function POST(request: Request) {
const body = await request.json();
if (typeof body.title !== "string" || !body.title.trim()) {
return Response.json({ error: "标题不能为空" }, { status: 400 });
}
return Response.json({ id: crypto.randomUUID(), title: body.title.trim(), done: false }, { status: 201 });
}即使 TypeScript 声明了类型,网络输入仍需在运行时检查。
- request.json 解析调用者提交的 JSON;解析本身也可能失败,生产实现需统一处理。
- 运行时检查 title 的类型和去空格结果,失败返回 400。
- 合法输入生成 id,并用 201 表示资源已创建。
合法标题得到 201 与任务 JSON;空字符串或非字符串得到 400;刷新页面不会保留该任务,因为示例没有数据库。
AI COLLABORATION
AI 如何参与
让 AI 先写接口契约和示例响应,再实现处理逻辑,避免前端后端各自猜格式。
推荐协作顺序
- 1
先让 AI 和你一起写 POST /api/tasks 的成功与失败响应。
- 2
契约确认后再生成 Route Handler,而且一次只实现一个方法。
- 3
不用前端,直接发送请求核对状态码和 JSON。
请用 Next.js App Router 和 TypeScript 实现最小 /api/tasks:GET 返回两条内存任务,POST 接收 { title: string } 并返回 201。先写接口契约和三种示例响应,再写 route.ts。暂时不接数据库。契约先于实现,GET/POST 职责分开,POST 对缺失标题返回明确 400,而不是永远 200。
人工检查清单
- 确认服务端没有相信客户端提供的 id、userId 或权限,错误响应结构一致。
- 让 AI 先写出方法、路径、输入、成功响应和失败响应,再生成处理函数。
- 绕过页面直接发送无效 JSON,确认服务端仍会拒绝并且响应不泄露内部信息。
COMMON TRAPS
常见误区
错误不是需要隐藏的失败,而是帮助你看清系统边界的证据。下面三类问题在 AI 辅助学习中最常出现。
先写代码,最后才猜接口格式
- 你会看到
- 前端期待 title,后端却返回 name,联调时双方不断改。
- 为什么发生
- 把前端数据当可信会让任何人通过自制请求伪造身份、越过限制或提交意外类型。
- 怎样纠正
- 实现前先写请求和响应示例,把字段名固定下来。
相信 TypeScript 会校验网络输入
- 你会看到
- 请求传来数字或空值,服务端仍按字符串处理。
- 为什么发生
- 直接返回内部对象会把密码哈希、密钥字段或未来新增字段无意暴露给调用者。
- 怎样纠正
- 类型只帮助开发,收到 JSON 后仍要做运行时校验。
所有情况都返回 200
- 你会看到
- 前端无法区分创建成功、输入错误和服务器故障。
- 为什么发生
- 只看页面报错会丢失真实状态码和服务端日志,路径、端口与业务异常无法区分。
- 怎样纠正
- 让 HTTP 状态码表达结果类别,正文再说明具体原因。
HANDS-ON
动手任务
实现 health 接口与一个接收文本、返回统计结果的 POST 接口。
- 完成 5.2「后端运行环境与依赖隔离」
跟着做
- 01
写出 GET 与 POST 的请求响应契约。
- 02
创建 app/api/tasks/route.ts。
- 03
实现 GET 返回内存数组。
- 04
实现 POST 读取 JSON、校验标题并返回 201。
- 05
分别发送正常、空标题和错误 JSON 请求,记录状态码。
可以从浏览器或命令行调用自己写的 API。
为 GET 增加 ?done=true 查询条件,并说明过滤属于哪一层。
离开本课前,自问四件事
- 路由、处理函数、请求参数和响应模型怎样连接?
- 为什么前端已经校验后,后端仍必须再次校验?
- TypeScript 为什么不能保证网络 JSON 真实符合类型?
- 接口调试时应同时收集客户端与服务端哪些证据?
确认完成后,会同步更新学习中心的课程学习进度。