
实际开发中t3code 被搜索或被讨论时背景通常不是“某个函数怎么写”而是“一套 TypeScript 全栈代码应该如何把前后端串起来”。t3code 这类代码库看起来像示例实际承担的是工程基线它把 Next.js、tRPC、Prisma、NextAuth 和 Tailwind 组织在同一个项目里让一次页面点击最终落到数据库查询再返回结构化 JSON整个过程由同一套类型系统约束。这篇文章以 t3code 所代表的 T3 技术栈代码库为分析对象做一次完整拆解。你会看到目录为什么这样分、环境变量为什么要在启动前校验、tRPC 的 router 和 procedure 到底在解决什么问题、登录态如何进入数据库请求上下文以及页面报错时应该先看哪一层日志。最后会给出常见问题排查表和生产部署检查清单方便把这类能力迁移到自己的项目中。1. t3code 是什么先理解这个代码库要解决什么问题1.1 从 create-t3-app 到 t3codeT3 技术栈如何组织起来T3 技术栈不是一个新语言而是一组前端、后端和数据库工具的组合。最核心的是 Next.js、TypeScript、tRPC、Prisma再加上 Tailwind CSS 与 NextAuth。create-t3-app 是生成这套组合的脚手架工具它负责把默认配置、基础目录和最小示例一次性准备好。t3code 可以理解为在这个基础上长出来的工程化代码库。脚手架只能给你一个“可以启动”的项目但工程化代码库会更进一步它会把业务示例、权限校验、环境变量校验、数据库模型和前端页面全部串起来。对学习者来说t3code 是可读、可改、可对照的参考实现对团队来说它是快速理解 T3 技术栈做事的样板。需要特别强调一个区别脚手架生成的是空壳t3code 这类代码库生成的是语义完整的业务骨架。前者回答“缺什么文件”后者回答“文件之间怎么协作”。这也是很多人把 create-t3-app 跑起来后又不知道下一步做什么的原因——缺少的正是 t3code 这类完整的联动示例。技术栈组合的核心价值不是“用了很新的框架”而是类型系统从数据库贯穿到页面。Prisma 根据 schema 生成数据库类型tRPC 让 API 调用返回类型可被前端直接引用zod 在服务端入口统一校验输入NextAuth 把登录用户写入请求上下文。这条链路一旦建立前端把postId写错类型编译阶段就会暴露而不是等接口返回 500 后再去抓包。1.2 t3code 的三类典型使用场景第一类是新手学习。适合按顺序阅读 router、procedure、middleware 和 Prisma schema理解一次请求从前端组件发出后经过_app.tsx的 Provider、Next.js API 路由、tRPC 服务端入口、router、数据库查询最后回到组件渲染的完整路径。第二类是快速搭建内部工具。很多管理后台、运营平台、个人站后台并不需要独立后端服务t3code 这种前后端同仓模式可以把类型定义集中在一个项目里减少跨服务联调成本。账号、数据库、页面都齐全很适合作为业务起点。第三类是团队内部规范参考。如果一个团队准备从 REST 或传统 MVC 转到类型安全全栈t3code 的目录划分、错误处理、权限中间件和验证边界都可以直接作为 code review 的讨论材料不必从零设计。1.3 适用读者与前置知识读者类型前置要求阅读重点Next.js 初学者会写 React 组件、知道页面路由数据请求链路、tRPC 调用方式后端转前端的开发者熟悉数据库和接口对 TypeScript 不熟Prisma、zod 校验、tRPC server 端前端想理解后端的开发者熟悉 TS 类型系统context、middleware、数据库访问团队技术负责人了解全栈项目开发流程目录结构、环境变量、部署检查建议先掌握浏览器开发工具 Network 面板、TypeScript 基础类型、以及 SQL 基本概念。如果之前只写过单一前端项目第一次接触 tRPC 时不要急着看全部源码先跑通一个简单 list 查询再研究权限和错误处理。2. 技术栈拆解为什么这套组合能把类型安全推到全栈2.1 tRPC远程过程调用如何变成“本地函数”tRPC 的核心思想是让客户端像调用本地函数一样调用服务端逻辑而不是手写 URL、手动维护请求参数和响应类型。它和 REST 的区别不在请求协议而在类型来源REST 接口的类型通常靠 OpenAPI 或工具生成tRPC 直接引用服务端导出的AppRouter类型。在 t3code 这类项目中服务端 router 定义好后会把typeof appRouter导出。前端通过createTRPCReactAppRouter()拿到带类型的客户端对象。调用api.post.list.useQuery()时返回值类型来自服务端 procedure 的.query回调而不是前端手写的 interface。import { createTRPCReact } from trpc/react-query; import type { AppRouter } from ~/server/api/root; export const api createTRPCReactAppRouter();这段代码的关键是import type。它只引入类型不引入执行逻辑也不会有服务端代码被打包进浏览器。前端只是借助同一个 TypeScript 类型在编译期获得参数类型、返回值类型和错误类型的推导。tRPC 也会带来一个隐藏约束服务端 router 的类型一旦变化所有引用了旧字段的页面都会报类型错误。这是好事但要求团队在重构时不能只改后端接口要连前端调用一起改。另一种常见误解是 tRPC 只能和 Next.js 一起用实际上它可以独立使用只是 t3code 的技术栈组合把它整合得更好。2.2 Prisma数据库访问层如何与类型系统衔接Prisma 负责数据库访问。它通过schema.prisma定义数据模型然后根据这个模型生成 Prisma Client。生成的客户端包含完整的 TypeScript 类型查询结果会被自动推导。model Post { id String id default(cuid()) title String content String authorId String createdAt DateTime default(now()) updatedAt DateTime updatedAt }在实际项目中这个模型还需要加索引、外键关联和合适的默认值。示例 schema 虽然简单但它体现了 t3code 的数据库设计思路先用 schema 描述数据结构再通过迁移或db push同步到数据库最后在 tRPC procedure 里直接使用ctx.db.post.findMany()。Prisma 的常见误区是把default(cuid())当作唯一全局主键策略。对小型项目没问题但大数据量、分表或性能敏感场景要单独评估主键策略。另外Prisma Client 默认并不自动执行generate所以每次修改 schema 后都要手动提醒自己先prisma generate再启动开发服务器否则类型会是旧的。2.3 NextAuth登录态如何进入请求上下文NextAuth 负责认证。t3code 这类项目中NotAuth 的意义不只是“提供登录页”而是把 session 写入 tRPC 的 context再由 context 决定某个 procedure 是否可以继续执行。在 t3code 常见代码里context 是这样组织的import { getServerSession } from next-auth; import { db } from ~/server/db; export const createTRPCContext async (opts: { headers: Headers }) { const session await getServerSession(); return { db, session, }; };context 创建发生在每个请求开始时。这里调用getServerSession()的目的是读取当前请求的 cookie 和会话信息得到当前用户或 null。之后所有 procedure 都能访问ctx.session但普通 publicProcedure 并不会强制校验它。NextAuth 与数据库的关系需要特别说清楚session 里的用户信息可以来自 OAuth 提供方也可以来自数据库。t3code 的常见做法是登录成功后再通过ctx.session.user的 id 去查数据库里的用户资料或业务数据而不是把用户完整信息全部塞进 JWT。2.4 T3 Env环境变量检查为什么要在启动时就做很多后端项目直到连接数据库失败才发现DATABASE_URL没配置。T3 Env 的解决方式是启动阶段用 zod 定义规则环境变量不合法时直接抛出明确错误。以常见env.mjs为例import { z } from zod; const server z.object({ DATABASE_URL: z.string().min(1), NEXTAUTH_SECRET: z.string().min(1), NEXTAUTH_URL: z.string().url(), }); const processEnv { DATABASE_URL: process.env.DATABASE_URL, NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, NEXTAUTH_URL: process.env.NEXTAUTH_URL, }; const parsed server.safeParse(processEnv); if (!parsed.success) { console.error(环境变量校验失败, parsed.error.flatten().fieldErrors); throw new Error(Invalid environment variables); } export const env parsed.data;这里的收益是快速失败。配置错误越早暴露排查范围越小。如果等到某个接口被调用时才报undefined你无法立刻判断是环境变量没传、写错了名字还是代码里读取时机太晚。要注意的是src/env.mjs里的校验规则只覆盖“启动 Node 服务时需要”的变量。浏览器端用到的变量要单独通过NEXT_PUBLIC_前缀暴露并谨慎处理避免服务端密钥泄漏到客户端。3. 同步代码、准备环境和安装依赖3.1 环境要求和版本基线工具建议说明Node.js与package.json的engines字段匹配的 LTS 版本版本过高或过低都会导致依赖编译异常包管理器优先使用 pnpmT3 技术栈生态对 pnpm 支持好 monorepo 扩展更顺数据库本地用 SQLite生产用 PostgreSQLSQLite 降低学习门槛 PostgreSQL 更贴近真实部署浏览器Chromium 内核或现代浏览器用于查看 Network 面板和调试 React安装前先检查当前 Node 版本node -v npm -v pnpm -v如果仓库指定了packageManager: pnpm...字段建议直接使用该版本。pnpm 的 lockfile 会和 npm 不同混用包管理器容易让依赖树不一致。遇到安装失败时第一步不是重试而是确认当前包管理器版本是否和项目packageManager声明一致。3.2 代码库的整体目录结构基于 create-t3-app 生成的 t3code 类项目典型结构如下. ├── prisma/ │ ├── schema.prisma │ └── dev.db ├── public/ ├── src/ │ ├── env.mjs │ ├── pages/ │ │ ├── _app.tsx │ │ └── index.tsx │ ├── server/ │ │ ├── api/ │ │ │ ├── trpc.ts │ │ │ └── routers/ │ │ │ ├── root.ts │ │ │ └── post.ts │ │ ├── auth.ts │ │ └── db.ts │ ├── styles/ │ │ └── globals.css │ └── utils/ │ └── api.ts ├── .env ├── .eslintrc.cjs └── package.json新项目如果使用 App Router则pages/会变成app/但 server 端 tRPC 入口和 Prisma db 模块的位置基本不变。目录价值在于分层页面组件只负责渲染和调用 hook业务逻辑放在 router 中数据库访问集中在 db.ts环境变量校验集中在 env.mjs。这样定位问题的路径就很短。3.3 从 clone 到 pnpm install 的完整步骤如果你有仓库访问权限同步到本地的命令大致如下git clone gitgithub.com:pingdotgg/t3code.git cd t3code pnpm install如果仓库是私有仓库或需要团队权限要先确认 GitHub 账号的 SSH key 已配置。不要直接复制 HTTPS 地址后遇到权限报错再反复换协议。clone 成功后先看根目录有没有.env.example。接下来安装依赖并生成 Prisma Clientpnpm install pnpm prisma generate如果package.json里没有直接暴露 prisma 脚本可以使用npx prisma generate这里的关键点是prisma generate必须在pnpm install之后执行因为它需要读取已安装的prisma/client。修改schema.prisma后也需要重新执行这一步否则代码里会出现“当前 Prisma Client 结构与 schema 不一致”的模糊错误。3.4 配置 .env 之前先确认哪几个值.env文件不会被提交到 git所以本地要手动创建。常用字段如下DATABASE_URLfile:./dev.db NEXTAUTH_URLhttp://localhost:3000 NEXTAUTH_SECRET请生成一个足够长的随机字符串使用 PostgreSQL 时DATABASE_URL的格式类似DATABASE_URLpostgresql://user:passwordlocalhost:5432/t3code?schemapublic三个字段的含义要分别确认。DATABASE_URL决定 Prisma 操作哪套数据库NEXTAUTH_URL在本地必须与 Next.js 启动地址保持一致否则 NextAuth 会拒绝回调NEXTAUTH_SECRET用于签名会话生产环境一定不能用代码仓库里的明文默认值。生成随机 secret 可以用openssl rand -base64 32创建.env之后执行pnpm dev之前最好先让 T3 Env 校验通过。如果控制台没有报环境变量错误再继续启动。4. 从数据库表到页面展示t3code 的典型功能开发路径4.1 用 Prisma Schema 定义业务模型假设要做一个简单的帖子列表功能。先修改prisma/schema.prismamodel Post { id String id default(cuid()) title String content String authorId String createdAt DateTime default(now()) updatedAt DateTime updatedAt }然后在数据库里应用pnpm prisma db push本地开发推荐先用prisma db push因为它直接同步 schema不产生迁移文件适合快速迭代。但一旦进入多人协作或生产环境就要改用prisma migrate dev生成迁移记录避免数据库结构和代码历史失去对应关系。这一步完成后检查 Prisma Studiopnpm prisma studio在浏览器中打开 Prisma Studio确认Post表是否出现。提前用界面验证数据表比启动应用后才发现字段对不上更高效。4.2 创建 tRPC Router把数据访问封装成 procedure在src/server/api/routers/post.ts中定义查询和写入import { z } from zod; import { createTRPCRouter, protectedProcedure, publicProcedure } from ../trpc; export const postRouter createTRPCRouter({ list: publicProcedure.query(async ({ ctx }) { return ctx.db.post.findMany({ orderBy: { createdAt: desc }, }); }), create: protectedProcedure .input( z.object({ title: z.string().min(1, 标题不能为空), content: z.string().min(1, 内容不能为空), }) ) .mutation(async ({ ctx, input }) { return ctx.db.post.create({ data: { title: input.title, content: input.content, authorId: ctx.session.user.id, }, }); }), });postRouter由两个 procedure 组成。list不需要登录直接返回帖子列表create要求登录通过protectedProcedure保证只有合法用户能写入。输入校验由 zod 在服务端完成前端传参少了或类型错了会返回可读错误而不是数据库抛出不可控异常。之后把 router 挂到根 routerimport { postRouter } from ./post; import { createTRPCRouter } from ../trpc; export const appRouter createTRPCRouter({ post: postRouter, }); export type AppRouter typeof appRouter;4.3 在页面组件中调用 procedure页面里通过api.post.list.useQuery()获取数据import { api } from ~/utils/api; export default function PostList() { const posts api.post.list.useQuery(); if (posts.isLoading) { return div加载中/div; } if (posts.isError) { return div请求失败{posts.error.message}/div; } return ( ul {posts.data.map((post) ( li key{post.id} h2{post.title}/h2 p{post.content}/p /li ))} /ul ); }这段代码有两点值得注意。第一posts.data的类型从服务端自动推导不需要手动声明Post[]。第二posts.error的类型包含服务端抛出的 tRPC 错误信息前端可以直接展示。这里的错误处理已经比普通 fetch 请求提供了更多上下文。4.4 数据流小结输入、校验、查询、渲染一次完整请求的实际路径是前端组件调用api.post.list.useQuery()。React Query 把请求发送到 Next.js API 路由下的 tRPC 入口。tRPC 服务端根据 path 找到post.listprocedure。执行 context 创建逻辑组装db和session。执行 procedure 回调Prisma 查询数据库。返回结构化成 JSON前端 hook 收到结果并触发重渲染。如果中间任何一步失败优先判断的是输入参数是否合法、context 是否缺少 session、Prisma 查询是否符合模型字段、返回值是否被前端错误使用。这四个判断维度可以覆盖大多数业务报错场景。5. 关键代码走读权限、输入校验和上下文传递5.1 服务端入口与 context 的组装t3code 类的项目中src/server/api/trpc.ts是所有 tRPC 逻辑的入口。它在这里初始化 tRPC 实例并把 context 处理与中间件绑定。import { initTRPC, TRPCError } from trpc/server; import { createTRPCContext } from ./context; const t initTRPC.contexttypeof createTRPCContext().create(); export const createTRPCRouter t.router; export const publicProcedure t.procedure;initTRPC.contextT()的作用是让 tRPC 实例知道每个请求的 context 类型。这样所有 procedure 里访问ctx.db或ctx.session时都能获得类型提示。context 不是全局单例而是每个请求创建一次避免请求之间互相污染数据。5.2 protectedProcedure 的中间件实现protectedProcedure通常由中间件定义const isAuthed t.middleware(({ ctx, next }) { if (!ctx.session?.user) { throw new TRPCError({ code: UNAUTHORIZED, message: 请先登录, }); } return next({ ctx: { session: ctx.session, user: ctx.session.user, }, }); }); export const protectedProcedure t.procedure.use(isAuthed);中间件在 procedure 执行之前运行。如果ctx.session不存在直接抛出UNAUTHORIZED后面的数据库查询根本不会执行。这里的关键不是“能不能登录”而是把权限判断前置避免在每个 procedure 里手动写if (!ctx.session) return。在真实项目中中间件还可以继续扩展比如增加角色判断、单设备登录限制、接口调用频率限制。但不要把所有逻辑都塞进一个中间件保持中间件单一职责否则排查权限问题时很难定位是哪一层拦截了请求。5.3 用 zod 做输入校验zod 的参数校验发生在 procedure 执行前tRPC 会自动序列化输入并交给 zod 解析。.input( z.object({ id: z.string().cuid(), title: z.string().max(200).optional(), }) )使用 zod 时要注意三个常见问题。第一不要把校验规则写得过于宽松比如只写z.string()会让空字符串也能进入数据库。第二不要在input里嵌套不可序列化对象比如Date实例或FiletRPC 的 JSON 序列化会失败。第三错误信息要写得能看懂直接返回 zod 默认的英文路径信息对前端排查没有帮助。5.4 客户端封装与报错方式客户端的utils/api.ts统一创建 tRPC 客户端import { httpBatchLink } from trpc/client; import { createTRPCReact } from trpc/react-query; import type { AppRouter } from ~/server/api/root; export const api createTRPCReactAppRouter(); export const apiClient api.createClient({ links: [ httpBatchLink({ url: /api/trpc, }), ], });httpBatchLink会把同一时间发出的多个请求合并为一个 HTTP 请求减少网络往返。学习阶段可以正常使用但要注意批量链接对耗时较长的上传或流式任务并不友好。如果后续做文件上传需要单独配置不经过 batch 的链接。在_app.tsx或根布局中还要把 QueryClient 和 tRPC Provider 包在组件外层否则 hook 无法工作。配置顺序是创建 QueryClient。创建 tRPC 客户端。用api.Provider包住应用。在 Provider 内部挂载QueryClientProvider。这两个 Provider 的顺序不能颠倒否则依赖注入关系会断掉。6. 运行验证从 dev 到 build 的预期结果6.1 dev 模式启动顺序与预期日志启动开发服务器pnpm dev正常启动后会看到类似下面的输出ready started server on 0.0.0.0:3000, url: http://localhost:3000如果项目没有dev脚本检查package.json的scripts字段。启动后不能只看页面能不能打开还要做三层验证页面是否正常渲染。打开浏览器 Network 面板确认/api/trpc/post.list请求返回 200。手动在数据库里插入一条记录刷新页面确认列表数据来自数据库而不是内存 mock。这里最容易踩的坑是页面能打开但数据是静态写死的一旦重启又消失。真正验证链路是否打通必须用数据库写入和查询来证明。6.2 类型检查、Lint 和构建提交代码前先跑pnpm typecheck pnpm lint pnpm buildtypecheck用于检查 TypeScript 类型lint用于检查代码风格build用于验证生产构建是否成功。在 t3code 这类代码库中build失败经常出现在三个地方失败位置常见原因处理方式tRPC router 报类型错误输入输出类型定义不一致先查 zod input 和 procedure 返回值Prisma Client 报错schema 修改后没 generate重新执行prisma generateNext.js 服务端组件引用客户端 hook在 Server Component 里用了 useQuery把数据获取拆到客户端组件6.3 验证数据库迁移状态与 API 页面跑完迁移后用下面的方式确认数据库状态pnpm prisma migrate status输出中如果出现Database schema is up to date说明迁移状态正常。如果出现 pending 状态就要重新执行迁移。tRPC 的 GET 请求也可以在浏览器地址栏里直接访问但查询参数格式与 tRPC 主版本有关不同版本之间存在差异。排查时不要死记 URL 格式而是在 Network 面板里复制一次正常请求再修改参数测试。这样能绕过“版本差异导致格式记忆错误”的问题。7. 常见问题排查从现象倒推根因7.1 环境变量不生效现象可能原因检查方式解决方案启动时报DATABASE_URL is not defined.env 文件缺失或字段名拼错打开 .env 检查键名从 .env.example 复制完整字段修改 .env 后不生效开发服务器缓存旧环境变量重启 dev server停止进程后重新pnpm dev部署后 API 连不上数据库生产环境没有注入对应变量检查部署平台环境变量配置重新配置密钥和安全组NEXTAUTH_SECRET太短导致 NextAuth 报错使用弱随机字符串查看启动日志用openssl rand -base64 32重新生成7.2 客户端找不到 tRPC 服务端如果页面报No query client found或请求一直 pending先确认 Provider 是否包裹正确。常见错误是在组件树上层用了QueryClientProvider但在更外层忘了加 tRPC Provider或者两个 Provider 的顺序错误。另一个常见原因是url配置错误。开发环境通常使用/api/trpc相对路径生产环境如果部署在子路径还要把basePath一并配置。不要直接写死http://localhost:3000/api/trpc这样换环境后接口一定报错。现象检查点请求 404查pages/api/trpc/[trpc].ts或app/api/trpc/[trpc]/route.ts是否存在请求 500查服务端日志里的错误堆栈请求一直 pending查浏览器 Network 面板的响应体和控制台错误7.3 Prisma 客户端与 Schema 不匹配报错信息通常类似The generated client does not match the current schema。原因是修改了schema.prisma但没有重新生成客户端。npx prisma generate如果 generate 后仍然报错检查是否有多个 Prisma Client 版本混用。删除node_modules和 lockfile 后重新安装是最后手段不要一开始就清依赖否则会把问题范围扩大。7.4 NextAuth 回调地址错误登录时出现redirect_uri报错或者登录成功后跳回错误地址最常见原因是NEXTAUTH_URL不匹配。本地开发必须与next dev启动的地址一致生产环境必须使用用户访问的公开地址。同时确认authOptions里的pages配置和callbacks是否正确。如果自定义了登录页路径NextAuth 会按照这个路径跳转回调函数返回 null 时登录流程会中断不一定有明确提示。7.5 TypeScript 类型报错的排查顺序如果页面或 router 报类型错误按下面的顺序排查先看prisma/schema.prisma是否已同步Prisma Client 是否已重新生成。再看 tRPC procedure 的输入和返回值有没有zod input与实际参数不一致。然后看前端 hook 的调用位置是否用了被删除的字段。最后检查createTRPCReactAppRouter()里引用的AppRouter是否来自当前文件。不要一看到类型报错就加as any。t3code 这类代码库的核心收益就是类型安全绕过类型等于放弃定位问题的能力。8. 学习环境与生产部署的差别8.1 数据库迁移策略本地开发用prisma db push很直接但生产环境必须使用迁移文件。pnpm prisma migrate dev迁移文件进入版本控制后部署时执行pnpm prisma migrate deploydb push和migrate deploy的区别在于db push强行把数据库同步到 schema 状态不保留历史migrate deploy按迁移记录逐步执行可以回滚和审计。生产环境不能用db push覆盖数据否则数据丢失风险极高。8.2 环境变量和密钥管理变量学习环境生产环境DATABASE_URL本地 SQLite 或 Docker PostgreSQL托管数据库连接串使用 TLSNEXTAUTH_SECRET任意随机字符串高强度密钥轮换机制NEXTAUTH_URLhttp://localhost:3000HTTPS 公网地址其他 API Key不放在 .env.example放在密钥管理服务或部署平台不要把生产密钥放在.env并提交到 git。即使仓库是私有仓库也建议加上.env到.gitignore并使用部署平台的 Secret 管理能力注入环境变量。8.3 日志、监控和回滚t3code 类项目默认不会带来完整监控体系。生产部署前至少要做到统一日志输出格式记录请求路径、用户 id、耗时和错误码。为 tRPC 请求增加错误上报记录 zod 校验失败和 TRPCError 的 code。保留上一版本镜像或构建产物便于回滚。数据库变更前做备份迁移失败时能从备份恢复。日志不只是给运维看的。在 t3code 的全栈架构里前端无法直接看到数据库错误服务端日志往往是唯一能确认问题层的依据。如果服务器没有日志排查会退化成盲改代码。9. 从 t3code 继续向下走容易踩的坑和最佳实践9.1 不要绕过 tRPC 直接写 APIt3code 的架构优势依赖 tRPC。如果某个功能绕过 tRPC在 Next.js 里直接写route handler调 Prisma并发请求的权限校验、类型校验和错误处理就会各自为政。遇到简单场景可以这样做但一旦发现重复代码增多就应该退回 tRPC 统一封装。9.2 输入输出类型要成对维护tRPC 的类型安全来自 router 定义但数据库模型和业务接口之间不会自动对齐。例如数据库存储userId但 API 返回时需要userName就要在 procedure 里做字段映射而不是直接返回整个数据库对象。避免把整个row直接返回给前端防止漏字段或暴露敏感字段。9.3 生产部署前检查清单检查项操作完成依赖版本确认 Node、pnpm、Prisma、Next.js 版本一致环境变量确认生产环境 DATABASE_URL、NEXTAUTH_SECRET、NEXTAUTH_URL 已注入数据库迁移执行prisma migrate deploy确认状态 up to date构建验证本地执行pnpm build并记录产物 hash权限检查确认敏感字段不会出现在公开 procedure 返回体中日志方案确认错误堆栈可以被正常收集回滚方案确认上一版本镜像或构建产物可快速恢复9.4 扩展方向t3code 的典型扩展路径有三个。第一个是 monorepo。如果前端、后台管理、文档站需要共享业务代码可以把 t3code 拆成apps和packages把数据库类型和共享工具放到独立包中。第二个是接入更完整的测试体系。t3code 类项目通常适合集成 vitest、Playwright 和 API 集成测试。测试重点不是每个组件而是 router 层的输入校验、权限拦截和数据库查询结果。第三个是增加后台任务和文件上传。这类场景与 tRPC 的长请求模型不完全匹配往往需要引入独立任务队列并把任务状态通过 tRPC subscription 或轮询返回给前端。做的时候要记住不是所有功能都适合在 tRPC 的请求响应模型里实现。t3code 的价值不在于它是不是一个严格定义的官方模板而在于它以可读、可跑、可改的方式展示了 T3 技术栈的完整协作方式。学习它时不要只满足于页面跑起来而是应该把 router、procedure、context、middleware、Prisma schema 和前端 Provider 之间的连接关系在脑海里画成一条清晰的链路。真正把这套链路的边界和原因讲清楚了换到任何基于 T3 技术栈的团队项目里都能快速定位问题、接手业务并保持类型安全带来的开发效率。