模块四:后端 API 与数据管理 | 第01讲:API 全链路——前端和数据库之间到底隔着什么?
本讲定位:把「点击保存」到「数据落库」之间的黑盒拆开,建立 HTTP、REST、Next.js App Router API 的可操作心智模型。
项目锚点:VibeNote 智能笔记(Next.js 14 + TypeScript + Tailwind + Drizzle + PostgreSQL)。
阅读线索:课程内容《5.1 前端和数据库之间到底隔着什么?API全链路讲透》与参考reference/advanced/07-backend-api.md。
一、开篇:为什么你不能让浏览器直连数据库?
很多 Vibe Coding 初学者会犯同一个直觉错误:既然 PostgreSQL 能存笔记,能不能在前端用连接串直接 INSERT?答案是绝对不行。
原因很简单,但后果很严重。前端代码运行在用户浏览器里,任何人打开开发者工具都能看到网络请求与打包后的逻辑;若把数据库地址、账号密码写进前端,等于把保险柜密码贴在小区公告栏。更糟的是,你无法在「用户侧」可靠地做权限校验——攻击者可以伪造请求,读写任意用户的数据。
因此,业界默认的分工是:
- 前端:展示 UI、收集输入、发起 HTTP 请求、处理响应与状态。
- API(后端边界):校验身份与参数、执行业务规则、与数据库交互、返回统一格式的结果。
- 数据库:持久化与查询,保证约束与事务。
参考材料里用「餐厅」类比很贴切:顾客(前端)不冲进厨房(数据库),而是通过服务员(API)下单。你的工程化目标,是让这条链路可观测、可测试、可迭代——这正是 AI-native 全栈里「人负责验收与边界,AI 负责填充实现」的落脚点。
二、HTTP 基础:一次请求里到底发生了什么?
HTTP(HyperText Transfer Protocol)是浏览器与服务端对话的语言。一次典型的「创建笔记」请求,至少包含:
- 请求行:方法 + 路径 + 协议版本,例如
POST /api/notes HTTP/1.1。 - 请求头:
Content-Type、Authorization、Cookie、缓存控制等元数据。 - 请求体(可选):JSON 形式的
{ "title": "...", "content": "..." }。
服务端处理完后返回响应:状态码、响应头、响应体(常为 JSON)。
2.1 用一张图理解全链路(VibeNote)
sequenceDiagram
participant U as 用户浏览器
participant N as Next.js 服务端
participant A as app/api Route Handler
participant D as PostgreSQL
U->>N: POST /api/notes (JSON Body)
N->>A: 路由匹配 route.ts 的 POST
A->>A: 解析 Body / 校验 / 鉴权(后续讲)
A->>D: SQL 通过 Drizzle 发出
D-->>A: 插入结果 / 错误
A-->>N: NextResponse.json(...)
N-->>U: 200 + { success, data }
2.2 方法、幂等与缓存直觉
| 方法 | 常见语义 | 幂等? | VibeNote 例子 |
|---|---|---|---|
| GET | 读取资源 | 是 | 拉取笔记列表 |
| POST | 创建资源 | 否 | 新建笔记 |
| PUT/PATCH | 更新资源 | PUT 常视为幂等 | 更新标题或正文 |
| DELETE | 删除资源 | 是 | 删除笔记 |
幂等指重复执行「结果状态一致」。POST 创建两次通常会生成两条记录,因此要考虑重复提交的防护(后续「校验与实战」讲展开)。
GET 可被浏览器或 CDN 缓存;携带用户私有数据的接口要谨慎使用 GET(查询参数会进日志与 Referer),敏感操作用 POST 并配合鉴权更稳妥。
三、REST API 设计:用「资源」组织 URL
REST(Representational State Transfer)不是唯一标准,但是 Next.js 生态里最常被 AI 默认生成的风格。核心习惯:
- URL 用名词表资源:
/api/notes、/api/notes/:id。 - HTTP 方法表动作:GET 列表/详情,POST 创建,PATCH 部分更新,DELETE 删除。
- 路径参数标识资源:
/api/notes/3的3是id。 - 查询参数表达过滤/分页:
/api/notes?q=rust&page=2&pageSize=20。
3.1 反模式(会让协作成本飙升)
/api/getNotes、/api/deleteNote:动词堆在路径上,方法语义重复。- 所有操作都 POST:短期能跑,长期难以用缓存、监控与文档表达意图。
- 返回结构每人一种:前端
if/else地狱,联调效率极低。
3.2 统一响应契约(强烈建议项目早期定死)
与参考文档一致,推荐约定:
- 成功:
{ success: true, data: T } - 失败:
{ success: false, error: { code?: string, message: string } }
这不是唯一标准,但一致比「优雅」更重要。你要做的是把契约写进 AGENTS.md 或 PRD,让 AI 每次生成接口都遵守。
flowchart LR
subgraph Client["前端"]
UI[页面/组件]
end
subgraph Edge["API 边界"]
R[Route Handler]
V[校验/鉴权]
end
subgraph Data["持久化"]
DB[(PostgreSQL)]
end
UI -->|HTTP JSON| R
R --> V
V -->|Drizzle/SQL| DB
DB -->|rows| V
V -->|统一 JSON| R
R --> UI
四、Next.js 14 App Router:API Route 怎么长出来?
在 App Router 下,API 文件放在 app/api/**/route.ts(或 .js)。文件系统即路由:
app/api/notes/route.ts→/api/notesapp/api/notes/[id]/route.ts→/api/notes/:id
每个文件导出与 HTTP 动词同名的函数:GET、POST、PATCH、DELETE。Next.js 按请求方法分发。
4.1 最小可运行示例(内存版,便于本地验证路由)
说明:下面示例不连数据库,只为验证「路由 + JSON 往返」。复制到新建 Next.js 14 项目的
app/api/notes/route.ts即可用curl调用。
// app/api/notes/route.ts
import { NextRequest, NextResponse } from "next/server";
type Note = { id: string; title: string; content: string; createdAt: string };
const memoryNotes: Note[] = [];
export async function GET() {
return NextResponse.json({ success: true, data: memoryNotes });
}
export async function POST(req: NextRequest) {
const body = await req.json().catch(() => null);
if (!body || typeof body.title !== "string") {
return NextResponse.json(
{ success: false, error: { message: "title 必填且为字符串" } },
{ status: 400 }
);
}
const note: Note = {
id: crypto.randomUUID(),
title: body.title,
content: typeof body.content === "string" ? body.content : "",
createdAt: new Date().toISOString(),
};
memoryNotes.unshift(note);
return NextResponse.json({ success: true, data: note }, { status: 201 });
}
本地验证命令(终端执行):
curl -X POST http://localhost:3000/api/notes ^
-H "Content-Type: application/json" ^
-d "{\"title\":\"第一条 VibeNote\",\"content\":\"# Hello\"}"
curl http://localhost:3000/api/notes
Windows PowerShell 可把
^换成反引号续行,或使用单引号包裹 JSON 的另一种写法。
4.2 Request/Response 里你要盯住的几个点
NextRequest:request.json()解析 body;request.url构造URL取searchParams。NextResponse.json:第二个参数可传{ status: 404 }。- Edge/Node 运行时:涉及数据库驱动时,通常选 Node runtime(视部署平台而定)。与 AI 协作时明确写「使用 Node runtime 访问 pg」可减少踩坑。
五、状态码:别只用 200 和 500
状态码是机器可读的第一层语义。建议 VibeNote 至少区分:
| 场景 | 状态码 | 说明 |
|---|---|---|
| 成功读取 | 200 | GET 列表/详情 |
| 创建成功 | 201 | POST 创建 |
| 无内容成功 | 204 | 某些删除/更新可无 body |
| 参数错误 | 400 | 校验失败 |
| 未登录 | 401 | 身份缺失或 token 无效 |
| 无权限 | 403 | 已登录但不可访问该资源 |
| 不存在 | 404 | id 不存在 |
| 冲突 | 409 | 唯一约束冲突等 |
| 服务端异常 | 500 | 未捕获错误,配合日志排查 |
实践建议:业务错误也用正确的 HTTP 状态码,同时在 body 里给可读 message,前端才能展示「人话」。
六、把全链路映射到 VibeNote 的迭代节奏
- 先打通
/api/notesGET/POST(内存或 mock)——验证前端表单、加载状态、错误态。 - 替换为 Drizzle + PostgreSQL——数据真落库(下一讲建模)。
- 加过滤、分页、搜索——查询参数与索引一起设计(第二讲、第七讲)。
- 加鉴权——未登录禁止写入(第四讲)。
这就是「API 全链路」在产品里的落地顺序:先能通,再正确,再安全,再性能。
七、与 AI 协作的提示词模板(可直接复制改)
把下面整段贴给 AI,并附上你的 PRD 片段:
你是资深 Next.js 14(App Router)工程师。请为项目 VibeNote 增加
app/api/notes/route.ts与app/api/notes/[id]/route.ts,要求:RESTful;统一返回{ success, data | error };对 POST/PATCH 做基础类型校验;给出可运行的 TypeScript 代码;错误使用合适 HTTP 状态码;不要在前端暴露数据库密钥。
你会发现,当你能描述边界,AI 产出会稳定一个数量级。
八、思考题(建议写下来)
- 为什么「统一响应格式」能显著降低团队协作成本?如果后端返回混用
data与result两种字段,前端会出现哪些具体工程问题? - 在什么情况下你会考虑 tRPC 而不是 REST?代价是什么?
- 对于「列表接口」,你会如何把分页参数设计成既兼容 AI 生成代码又方便前端封装的形态?
九、本节小结
- 前端与数据库之间必须隔一层 API,核心是安全与业务规则收口。
- HTTP 负责传输,REST 负责资源化表达,Next.js Route Handler 负责把 URL 映射到函数。
- 状态码 + 统一 JSON 契约是前后端联调的「通用语言」。
- 迭代顺序:mock → 真库 → 校验 → 鉴权 → 观测。
十、下一讲预告
第02讲:数据库建模实战——主键、外键、索引、事务,一次讲透
我们将把 VibeNote 的 users / notes / tags / note_tags 落到 PostgreSQL:用 Docker 或 Supabase 起实例,讲清主键策略、外键与引用完整性、索引与查询模式、事务在「笔记+标签」写入中的用法,并给出可直接执行的 SQL 与建模检查清单。把本讲的 /api/notes 从内存版换成「真落库」,你的全栈闭环就成立了。