Skip to content
返回构建教程

全栈接线

连接前端、API 和数据库,让按钮点击往返 DB 并返回。

为什么

'全栈接线'是产品变真实的时刻——用户点击、数据流到 DB、结果显示屏幕。此前你有漂亮 UI 模型和 DB schema 但无产品。接线是多数 bug 所在:前端发错字段名、API 不验证、ORM 静默丢列、用户看通用错误。有纪律接线用端到端类型安全(前端类型从 API schema 生成、API 类型从 DB schema),重命名是全栈编译错误。没有它,三层漂移且每次集成是调试会话。

何时使用

UI 和 DB schema 存在后,第一个用户功能之前。约 2-4 小时。仅纯静态站可跳过。

如何做

1. 从服务端生成 API 类型并在客户端导入
   - tRPC:类型从服务端 router 自动流到客户端
   - REST:openapi-typescript 从 OpenAPI spec 生成类型
   - 永不手写客户端类型复制服务端的

2. 用带类型查询的 ORM(Prisma、Drizzle)让 DB 结果有类型
   - prisma.user.findUnique 返回类型化 User,非 any
   - schema 变更 → prisma generate → 每个坏查询的类型错误

3. 先端到端接一个功能作模板
   - 按钮 → fetch /api/items → prisma.item.create → 返回类型化 Item → 渲染
   - 这成每个未来功能的复制粘贴模式

4. 加用 zod 验证输入的 server action 或 route handler
   - const schema = z.object({ name: z.string().min(1) })
   - 碰 DB 前解析;返回 UI 可显示的类型化错误

5. 测往返:Playwright 测试点按钮并断言 DB 行存在
   - 这是唯一抓跨三层接线 bug 的测试

示例

// 服务端:app/api/items/route.ts(Next.js + Prisma + zod)
import { prisma } from "@/lib/prisma";
import { z } from "zod";
const schema = z.object({ name: z.string().min(1), qty: z.coerce.number().int().positive() });
export async function POST(req: Request) {
  const input = schema.parse(await req.json());  // 非法即抛
  const item = await prisma.item.create({ data: input });  // 类型化 Item
  return Response.json(item);  // 类型化响应
}

// 客户端:类型从服务端流
import type { Item } from "@prisma/client";  // 单一事实源
async function createItem(input: { name: string; qty: number }): Promise<Item> {
  const res = await fetch("/api/items", { method: "POST", body: JSON.stringify(input) });
  return res.json();
}

常见问题

  • ⚠手写客户端类型与服务端漂移,字段重命名静默坏客户端。
  • ⚠服务端跳过输入验证因'表单验证了',直接 API 调用绕过所有检查。
  • ⚠用 prisma.$queryRaw 模板字符串,丢类型安全并开 SQL 注入。
  • ⚠无端到端测试,接线 bug(错字段名、缺列)仅手工 QA 才现。

最佳实践

  • ✓用 tRPC 或生成客户端让类型从服务端流到客户端零手工同步。
  • ✓服务端用 zod 验证,从 zod schema 推客户端类型(z.infer<typeof schema>)。
  • ✓保持一个功能端到端作'黄金路径'每新功能复制——一致性胜新颖。
  • ✓功能'完成'前写 Playwright e2e 测试,接线 bug 立即现。