t3code 全栈开发实战:Next.js + tRPC + Prisma 类型安全指南

📅 发布时间:2026/10/9 11:16:58
t3code 全栈开发实战:Next.js + tRPC + Prisma 类型安全指南
1. 从“t3code”这个标题说起它到底是什么第一次看到“t3code”这个词我脑子里蹦出来的第一反应是——这大概率是个技术圈的缩写或者代号而不是某个大众消费品。事实也确实如此。在开发者社区里t3code 通常指的是一类围绕“T3 技术栈”构建的代码实践、脚手架或者项目模板。所谓 T3 技术栈核心组合是Next.js TypeScript tRPC Prisma Tailwind CSS NextAuth这套组合在 2022 年前后由社区推出来之后迅速成为全栈 TypeScript 项目里最受欢迎的“全家桶”之一。那 t3code 能做什么简单说它解决的是“从零搭一个类型安全的全栈应用”这件事。传统做法里前端调后端接口你得手写 fetch、手写类型定义、手写校验前后端类型对不上是家常便饭。t3code 这套东西的价值就在于端到端的类型安全。你在数据库 schema 里改一个字段前端调用处立刻飘红编译期就能发现而不是等到线上跑出 500 才后知后觉。这篇文章适合谁看如果你是有一定 React 和 TypeScript 基础想搭一个正经的全栈项目又不想在“胶水代码”上浪费时间的开发者那这篇内容就是写给你的。如果你是完全的新手也没关系我会把每个环节的“为什么”讲清楚你照着抄作业也能跑起来。接下来我会从整体设计思路、核心细节、实操过程、常见问题四个维度把 t3code 这套东西掰开揉碎讲一遍中间穿插我自己踩过的坑和实测有效的技巧。2. 整体设计与思路拆解为什么是这套组合2.1 类型安全是核心诉求不是噱头很多人第一次接触 t3code 会觉得“不就是把几个库拼一起吗”但真正用过之后你会发现这套组合的每一个选择都是围绕“类型安全”这个核心目标来的。传统 REST 架构里前端和后端是两套独立的类型系统你写一个/api/user接口返回的 JSON 结构全靠文档和口头约定。一旦后端改了字段名前端不跟着改运行时才报错。t3code 用 tRPC 把这个问题从根上解决了。tRPC 的核心机制是后端定义 procedure前端直接 import 类型。你不需要生成任何 SDK也不需要写 OpenAPI 文档TypeScript 的编译器就是你的接口契约。我实测下来一个中等规模的项目光“前后端类型对不上”这一类 bug 就能减少八成以上。2.2 各组件分工明确各司其职把这套栈拆开看每个组件都有清晰的职责边界组件职责为什么选它Next.js全栈框架路由 SSR API一个项目搞定前后端部署简单TypeScript类型系统整个栈的类型安全基础tRPC前后端通信零代码生成端到端类型推断Prisma数据库 ORMschema 即类型迁移管理方便Tailwind CSS样式方案原子化不用来回切文件NextAuth认证和 Next.js 深度集成开箱即用这个分工的好处是你不需要在“选哪个库”上纠结社区已经帮你验证过这套组合的兼容性。我见过太多项目死在“库和库之间打架”上t3code 的价值之一就是帮你省掉这部分试错成本。2.3 和传统方案的对比省掉的是胶水不是能力有人会问我用 Express React 也能做为什么要换答案不是“能力更强”而是“胶水更少”。传统方案里你要写 API 路由、写请求封装、写类型定义、写参数校验这些代码不产生业务价值但占了你大量时间。t3code 把这些环节压缩了参数校验用 Zod 定义一次前后端共用类型定义Prisma schema 生成不用手写请求封装tRPC 自动处理不用写 fetch省下来的时间你可以花在真正的业务逻辑上。这是我个人最看重的一点。3. 核心细节解析与实操要点3.1 项目初始化create-t3-app 的正确打开方式官方提供的脚手架是create-t3-app用起来很简单npm create t3-applatest my-app执行之后会有一系列交互式选项这里有几个关键选择需要注意TypeScript必选这是整套栈的基础tRPC必选核心通信层Prisma建议选除非你用别的 ORMNextAuth看需求需要登录就选Tailwind建议选省得自己配App Router vs Pages Router新项目一律选 App Router注意脚手架生成的代码是“最小可用”版本不是“生产就绪”版本。它帮你搭好骨架但业务逻辑、错误处理、日志这些还得自己补。我踩过的一个坑是早期版本脚手架默认用 Pages Router后来 App Router 成熟了才切换。如果你现在建项目一定确认选的是 App Router否则后面迁移成本很高。3.2 Prisma schema 设计类型安全的源头Prisma 的 schema 文件是整个项目类型安全的起点。你在这里定义的每一个 model都会生成对应的 TypeScript 类型。举个实际例子model Post { id String id default(cuid()) title String content String? published Boolean default(false) authorId String author User relation(fields: [authorId], references: [id]) createdAt DateTime default(now()) updatedAt DateTime updatedAt }这个 schema 定义完之后Prisma 会生成Post类型你在 tRPC procedure 里直接用前端也能推断出来。改字段名的时候所有引用处都会报错这就是类型安全的价值。实操要点default(cuid())比自增 ID 更适合分布式场景updatedAt自动维护更新时间省得手动写关系字段用relation显式声明别偷懒3.3 tRPC procedure 的三种类型query、mutation、subscriptiontRPC 的 procedure 分三种用错了会很别扭query读操作比如获取列表、详情mutation写操作比如创建、更新、删除subscription实时订阅比如聊天消息新手最容易犯的错是把写操作写成 query。虽然技术上能跑但语义不对而且 query 默认会被缓存和预取写操作走 query 会导致意料之外的行为。我的建议是只要涉及数据变更一律用 mutation。3.4 Zod 校验一次定义前后端共用Zod 是这套栈里的“隐形功臣”。你在 tRPC procedure 的 input 里用 Zod 定义校验规则前端调用时自动获得类型提示后端自动校验参数。比如const createPostSchema z.object({ title: z.string().min(1).max(100), content: z.string().optional(), }); export const postRouter createTRPCRouter({ create: protectedProcedure .input(createPostSchema) .mutation(async ({ ctx, input }) { return ctx.db.post.create({ data: { ...input, authorId: ctx.session.user.id, }, }); }), });前端调用api.post.create.useMutation()时传入的参数类型就是createPostSchema推断出来的。传错类型、少传字段编译期就报错。提示Zod schema 建议单独抽到一个文件里前后端都能 import避免重复定义。4. 实操过程与核心环节实现4.1 环境准备与依赖安装先把基础环境搭好。Node.js 版本建议 18 以上pnpm 作为包管理器比 npm 快且对 monorepo 友好node -v # 确认 18 npm install -g pnpm然后创建项目pnpm create t3-applatest my-t3-app cd my-t3-app pnpm install安装完成后先跑一下pnpm dev确认默认页面能打开。这一步别跳过我见过有人装完直接写业务结果环境有问题排查半天。4.2 数据库配置本地用 SQLite生产用 PostgreSQL开发阶段用 SQLite 最省事不用装数据库服务DATABASE_URLfile:./dev.db生产环境建议 PostgreSQL改DATABASE_URL即可。Prisma 的好处是换数据库只需要改连接串和 provider业务代码不用动。配置完之后跑迁移pnpm prisma migrate dev --name init pnpm prisma generatemigrate dev会生成迁移文件并应用到数据库generate会重新生成 Prisma Client 类型。每次改完 schema 都要跑这两条命令否则类型对不上。4.3 写第一个 tRPC procedure从数据库到前端假设我们要做一个“文章列表”功能。先在server/api/routers/post.ts里定义import { z } from zod; import { createTRPCRouter, publicProcedure } from ../trpc; export const postRouter createTRPCRouter({ list: publicProcedure .input(z.object({ limit: z.number().min(1).max(100).default(10) })) .query(async ({ ctx, input }) { return ctx.db.post.findMany({ take: input.limit, orderBy: { createdAt: desc }, }); }), });然后在server/api/root.ts里注册export const appRouter createTRPCRouter({ post: postRouter, });前端调用const { data, isLoading } api.post.list.useQuery({ limit: 20 });整个过程不需要写任何 fetch、不需要手写类型、不需要写接口文档。这就是 t3code 的核心体验。4.4 认证集成NextAuth 的配置要点如果项目需要登录NextAuth 的配置有几个关键点NEXTAUTH_SECRET必须设置生产环境用随机字符串Provider 选一个就行新手建议先用 Credentials 或 GitHubsession策略建议用jwt省得配 session 表配置完之后protectedProcedure会自动校验登录状态未登录直接抛错。这个机制比手动写中间件省事得多。4.5 部署Vercel 是最省心的选择t3code 项目部署到 Vercel 几乎是零配置。数据库用 Neon 或 Supabase 的 PostgreSQL环境变量在 Vercel 后台配好push 代码自动部署。我实测下来从本地开发到线上跑通整个流程不超过半小时。注意SQLite 不能用于 Vercel 部署因为文件系统是临时的。生产环境必须用远程数据库。5. 常见问题与排查技巧实录5.1 类型推断失效先检查这几个地方tRPC 的类型推断偶尔会“失灵”表现为前端调用时input类型变成any。排查顺序确认pnpm prisma generate跑过确认server/api/root.ts里注册了对应的 router重启 TypeScript 服务VS Code 里CmdShiftP→ Restart TS Server检查 tsconfig 的strict是否开启九成以上的类型问题重启 TS 服务就能解决。5.2 数据库迁移冲突别慌有套路多人协作时迁移文件冲突很常见。处理流程先git pull拉最新代码跑pnpm prisma migrate dev看是否有冲突有冲突就pnpm prisma migrate reset重置本地库仅限开发环境重新跑迁移警告migrate reset会清空数据库生产环境绝对不能用。5.3 常见问题速查表问题现象可能原因解决方法前端调用报 404router 没注册检查 root.ts类型推断为 anyPrisma Client 没生成跑 prisma generate登录状态拿不到session 配置错误检查 NextAuth 配置部署后数据库连不上环境变量没配Vercel 后台补上样式不生效Tailwind 没扫描到检查 content 配置5.4 独家避坑技巧几个我从实际项目里总结出来的经验Zod schema 抽公共文件前后端共用避免定义两份tRPC procedure 按业务分文件别全塞一个文件后期维护痛苦Prisma 用select而不是全字段返回减少数据传输也避免敏感字段泄露开发环境开prisma studiopnpm prisma studio可视化看数据调试神器最后分享一个我个人的习惯每次改完 schema先跑prisma generate再跑tsc --noEmit做一次全量类型检查。这一步能提前发现大部分问题比等到运行时再排查省事得多。t3code 这套栈的价值说到底就是让类型系统帮你干活你只要把 schema 和 procedure 定义清楚剩下的交给编译器。