4.1 API 全链路——前端和数据库之间到底隔着什么?

0 阅读7分钟

模块四:后端 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)是浏览器与服务端对话的语言。一次典型的「创建笔记」请求,至少包含:

  1. 请求行:方法 + 路径 + 协议版本,例如 POST /api/notes HTTP/1.1
  2. 请求头Content-TypeAuthorizationCookie、缓存控制等元数据。
  3. 请求体(可选):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/33id
  • 查询参数表达过滤/分页/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/notes
  • app/api/notes/[id]/route.ts/api/notes/:id

每个文件导出与 HTTP 动词同名的函数:GETPOSTPATCHDELETE。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 里你要盯住的几个点

  • NextRequestrequest.json() 解析 body;request.url 构造 URLsearchParams
  • NextResponse.json:第二个参数可传 { status: 404 }
  • Edge/Node 运行时:涉及数据库驱动时,通常选 Node runtime(视部署平台而定)。与 AI 协作时明确写「使用 Node runtime 访问 pg」可减少踩坑。

五、状态码:别只用 200 和 500

状态码是机器可读的第一层语义。建议 VibeNote 至少区分:

场景状态码说明
成功读取200GET 列表/详情
创建成功201POST 创建
无内容成功204某些删除/更新可无 body
参数错误400校验失败
未登录401身份缺失或 token 无效
无权限403已登录但不可访问该资源
不存在404id 不存在
冲突409唯一约束冲突等
服务端异常500未捕获错误,配合日志排查

实践建议:业务错误也用正确的 HTTP 状态码,同时在 body 里给可读 message,前端才能展示「人话」。


六、把全链路映射到 VibeNote 的迭代节奏

  1. 先打通 /api/notes GET/POST(内存或 mock)——验证前端表单、加载状态、错误态。
  2. 替换为 Drizzle + PostgreSQL——数据真落库(下一讲建模)。
  3. 加过滤、分页、搜索——查询参数与索引一起设计(第二讲、第七讲)。
  4. 加鉴权——未登录禁止写入(第四讲)。

这就是「API 全链路」在产品里的落地顺序:先能通,再正确,再安全,再性能


七、与 AI 协作的提示词模板(可直接复制改)

把下面整段贴给 AI,并附上你的 PRD 片段:

你是资深 Next.js 14(App Router)工程师。请为项目 VibeNote 增加 app/api/notes/route.tsapp/api/notes/[id]/route.ts,要求:RESTful;统一返回 { success, data | error };对 POST/PATCH 做基础类型校验;给出可运行的 TypeScript 代码;错误使用合适 HTTP 状态码;不要在前端暴露数据库密钥。

你会发现,当你能描述边界,AI 产出会稳定一个数量级。


八、思考题(建议写下来)

  1. 为什么「统一响应格式」能显著降低团队协作成本?如果后端返回混用 dataresult 两种字段,前端会出现哪些具体工程问题?
  2. 在什么情况下你会考虑 tRPC 而不是 REST?代价是什么?
  3. 对于「列表接口」,你会如何把分页参数设计成既兼容 AI 生成代码又方便前端封装的形态?

九、本节小结

  • 前端与数据库之间必须隔一层 API,核心是安全与业务规则收口。
  • HTTP 负责传输,REST 负责资源化表达,Next.js Route Handler 负责把 URL 映射到函数。
  • 状态码 + 统一 JSON 契约是前后端联调的「通用语言」。
  • 迭代顺序:mock → 真库 → 校验 → 鉴权 → 观测。

十、下一讲预告

第02讲:数据库建模实战——主键、外键、索引、事务,一次讲透
我们将把 VibeNote 的 users / notes / tags / note_tags 落到 PostgreSQL:用 Docker 或 Supabase 起实例,讲清主键策略、外键与引用完整性、索引与查询模式、事务在「笔记+标签」写入中的用法,并给出可直接执行的 SQL 与建模检查清单。把本讲的 /api/notes 从内存版换成「真落库」,你的全栈闭环就成立了。