ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

T3 Stack 全栈指南:Next.js + TypeScript + tRPC + Prisma 一体化脚手架

2026/9/8 4:35:24 拓冰建站 浏览量
T3 Stack 全栈指南:Next.js + TypeScript + tRPC + Prisma 一体化脚手架 这次我们来看 T3 Stack。如果你的项目需要同时搞定前端页面、后端接口、数据库访问和类型校验直接在 Next.js 里自己拼技术选型其实是一件很费精力的事路由怎么组织、接口层用什么、ORM 选哪个、类型怎么保证前后端一致。T3 Stack 就是为这个问题给出的组合答案。它由 t3.gg 的 Theo 等人在社区中推广落地形式是 create-t3-app 这个开源脚手架。T3 Stack 不是新的框架而是把 Next.js、TypeScript、tRPC、Prisma、Tailwind CSS、NextAuth 这些成熟工具按一套默认约定组合起来让你一条命令拿到一个可运行的全栈项目骨架。核心特点可以快速列一下第一一条命令初始化项目并且默认开启 TypeScript 严格模式类型约束从第一行代码就开始生效第二用 tRPC 做接口层前端调用后端方法时不需要手写 REST URL前后端类型由编译器整体推导接口变更不靠文档提醒而是靠类型报错直接暴露第三ORM 可以在 Prisma 和 Drizzle 之间选择建表、迁移、查询都纳入了工程化流程第四UI 层默认集成 Tailwind样式迭代速度快写页面不需要频繁切换 CSS 文件第五整个链路看起来重但实际开发不需要高配 GPU普通开发机就能跑对显卡完全没要求。这篇文章会带你完成环境准备与版本检查、使用 create-t3-app 初始化一个全栈项目、读懂 T3 Stack 的项目目录、开发一个 tRPC 接口并用前端页面验证、接入 Prisma 做数据建模和数据库迁移、跑一次全栈联调测试、观察开发服务器的资源占用、整理常见报错排查清单。内容偏实战直接按“能不能用、怎么跑通、失败怎么查”的顺序来写。1. 核心能力速览能力项说明项目类型React 全栈应用脚手架组合 Next.js TypeScript tRPC Prisma / Drizzle Tailwind CSS来源社区开源项目T3 社区维护Theo 是主要推广者之一主要功能一条命令初始化全栈项目内置接口层、数据库访问层、样式方案和可选认证方案运行环境Node.js 18 及以上Windows / macOS / Linux 均可显存需求无 GPU 要求纯 CPU 开发环境即可运行启动方式pnpm dev/npm run dev等命令启动 Next.js 开发服务器默认访问地址http://localhost:3000端口被占用时 Next.js 通常会自动尝试 3001API 能力内置 tRPC 协议接口也支持 Next.js Route Handler 自定义 API批量任务脚手架本身不带任务队列但可以通过脚本方式批量操作数据库或调用接口适合场景全栈 Web 应用、中后台系统、内部工具、快速原型、React 全栈学习项目T3 Stack 的定位不是“面试框架”而是解决一个非常具体的问题在一个 TypeScript 全栈项目里如何让前端到后端再到数据库的整条链都类型安全。它的选择不一定是最新潮的但一定是当前社区里验证过的组合。如果你的目标是快速做一个带数据库交互的 Web 应用同时希望代码结构干净、后续方便维护这个技术栈可以节省大量选型和配置时间。2. 适用场景与使用边界T3 Stack 适合以下几类场景第一快速原型和全栈工具比如内部管理后台、报表系统、自动化配置页面这类项目核心是“前端表单 后端接口 数据表”的标准组合用 T3 Stack 默认结构非常合适。第二中小型应用团队人数不多前端和后端由同一个人或同一个小团队维护TypeScript 全栈可以让前后端共用类型定义减少联调成本。第三学习 React 全栈想理解 Next.js App Router 怎么做服务端渲染、tRPC 怎么把函数调用变成 HTTP 请求、Prisma 怎么把 TypeScript 对象映射成 SQLT3 Stack 是一个清晰的参考资料。但也有不适合的情况。如果项目已经有一个独立的 Java、Go、Python 后端只缺一个前端工程那 T3 Stack 里的 tRPC 可能和现有后端冲突强行接入会绕很多弯如果做的是纯静态博客、落地页不需要数据库和接口直接使用 Next.js 基础模板更轻如果对前端包体积极端敏感比如希望首屏加载尽量小那 tRPC 客户端和全套依赖会带来额外体积需要评估收益是否大于成本。使用边界上要特别注意三点。create-t3-app 是开源项目生成的代码你拥有但上游依赖各自有许可证商用前建议检查依赖列表的授权情况。项目会生成.env文件里面可能放数据库连接串和 NextAuth 密钥这个文件不能提交到 Git否则等于把数据库密码公开。如果项目将来要面向真实用户尤其是涉及用户资料、订单、手机号等隐私数据必须做鉴权、访问控制、操作日志和隐私合规审查不能只开着公开接口方便调用。顺手提醒一句本地写演示项目时数据库用测试数据就好不要拿真实用户信息去填充开发库。正式发布前把开发数据清理一遍避免测试数据被搜索引擎抓走或泄露。3. T3 Stack 本地部署环境准备开始之前先确认本机环境。T3 Stack 的安装工具是 Node.js所以第一步是确认 Node 版本。建议使用 Node.js 18 或更高版本具体版本要求以 create-t3-app 官方文档为准。可以使用下面命令检查node -v npm -v如果你习惯用 pnpm可以额外安装 pnpm。pnpm 对依赖的磁盘占用控制更好T3 社区中也比较常见npm install -g pnpm pnpm -v数据库方面实验阶段推荐 SQLite。SQLite 是一个文件数据库不需要单独安装数据库服务Prisma 会直接操作本地文件最省事。等应用上线再切换到 PostgreSQL 或 MySQL。这一点对快速跑通流程非常重要你不需要先装一个数据库建库、配账号、开端口直接初始化项目就能开始操作。接下来确认网络能正常访问 npm registry。如果安装依赖时经常超时可以在项目目录下建一个.npmrc文件或者执行一次性的 registry 配置将 npm 源切换到国内镜像。这属于常规网络优化不是特殊工具npm config set registry https://registry.npmmirror.com最后检查磁盘空间。Node 项目安装依赖通常会占用几百 MB 以上不同依赖版本差异较大以实际机器为准。至少预留 1GB 以上空间会更稳。不需要 GPU不需要 CUDA不涉及显存这一点和 AI 模型类项目不同。4. 使用 create-t3-app 初始化项目打开终端进入你准备存放项目的目录执行pnpm create t3-applatest my-t3-app如果使用 npmnpm create t3-applatest my-t3-app执行后create-t3-app 会让你选择需要启用的模块常见选项包括 TypeScript、Tailwind CSS、tRPC、Prisma、Drizzle、NextAuth。如果你只是想快速体验全套流程建议开始先把核心选项打开。这样会生成带 Tailwind、tRPC、Prisma 和 NextAuth 的完整模板。如果你只想做最小接口练习也可以只选 tRPC 加 Prisma后续再手动补其他模块。交互过程中还会询问项目名称、是否使用 Git 初始化等。项目名可以直接用终端参数指定不过要在交互提示里确认。初始化完成后看到提示信息说明项目骨架已经生成。进入项目目录确认依赖已经安装完毕。不同版本行为不同如果初始化过程中没有自动安装依赖则手动执行cd my-t3-app pnpm install随后启动开发服务器pnpm dev启动成功后浏览器打开 http://localhost:3000 。第一次访问会看到 T3 Stack 默认首页页面主体是模板介绍和一些示例区块。这一步如果页面渲染正常说明 Next.js、Tailwind、tRPC 的基础链路已经跑通。5. 项目结构与核心目录说明T3 Stack 生成的项目目录比普通 Next.js 模板更规整。下面是一个常见结构示例不同版本会有些微差异但核心目录基本保持一致my-t3-app/ ├── prisma/ │ ├── schema.prisma │ └── dev.db ├── src/ │ ├── app/ │ │ ├── api/ │ │ ├── layout.tsx │ │ └── page.tsx │ ├── server/ │ │ ├── api/ │ │ │ ├── root.ts │ │ │ ├── routers/ │ │ │ │ └── post.ts │ │ │ └── trpc.ts │ │ └── db.ts │ ├── trpc/ │ │ ├── react.tsx │ │ └── server.ts │ └── styles/ │ └── globals.css ├── .env ├── .env.example ├── package.json └── tsconfig.json接下来逐个说明关键文件的作用。prisma/schema.prisma是数据库模型定义文件所有的数据表结构都在这里用 Prisma Schema 语法声明。改完 schema 后执行迁移命令就会在数据库里生成对应的表。.env存放环境变量最重要的是DATABASE_URL数据库连接串和 NextAuth 需要的密钥.env.example是提交到 Git 的示例文件只放变量名不放真实值方便其他开发者复制后填写。src/server/api/trpc.ts是 tRPC 初始化核心文件里面会创建 tRPC context、router、procedure 等基础对象。src/server/api/root.ts是根路由所有业务 router 都要挂载到这里前端才能通过api.xxx.yyy调用。src/server/db.ts负责初始化 PrismaClient 实例后续所有数据库查询都通过上下文里的ctx.db来执行。src/trpc/react.tsx是前端 React 绑定包装了 tRPC 客户端并提供api对象给组件调用。理解这个结构之后新增一个业务模块的基本路径就是在prisma/schema.prisma里加 model在src/server/api/routers/下新建 router 文件把 router 挂载到src/server/api/root.ts再在前端组件里调用对应 procedure。整个过程不需要手写 Controller、Service、URL 映射路径短且清晰。6. tRPC 接口开发与 API 验证了解了目录结构后现在实际开发一个 tRPC 接口。T3 Stack 默认的root.ts里通常有一个示例 procedure可以在它的基础上扩展。下面是一个常见的hello接口定义输入一个 name 参数返回问候语和时间戳// src/server/api/root.ts import { z } from zod; import { createTRPCRouter, publicProcedure } from ~/server/api/trpc; export const appRouter createTRPCRouter({ hello: publicProcedure .input(z.object({ name: z.string().min(1) })) .query(({ input }) { return { message: Hello, ${input.name}!, now: new Date().toISOString(), }; }), }); export type AppRouter typeof appRouter;这里有两个关键点。第一input用 Zod 声明运行时校验和 TypeScript 类型推导来自同一个定义不会出现“校验规则和类型不一致”的问题。第二query表示这是一个查询操作适合读数据如果是要写数据通常用mutation。当你把文件保存好Fast Refresh 会自动更新类型前端立刻就能感知到接口签名。前端调用方式很直接。在客户端组件中使用api.hello.useQueryuse client; import { api } from ~/trpc/react; export function HelloBox({ name }: { name: string }) { const hello api.hello.useQuery({ name }); if (hello.isLoading) { return div加载中.../div; } if (hello.isError) { return div请求失败{hello.error.message}/div; } return div{hello.data.message}/div; }把这个组件放进src/app/page.tsx后浏览器访问首页页面应该能看到“Hello, xxx!”的输出。如果显示加载中再看终端日志如果显示请求失败优先检查 tRPC Provider 是否已经在根布局中挂载。T3 Stack 模板默认会在app.tsx或RootLayout中提供 Provider自己新建组件时不要漏掉这层包裹。如果不想走 tRPC 页面链路也可以加一个独立的 Next.js Route Handler 做健康检查。这个接口适合用于部署后的连通性验证不依赖 tRPC 前缀直接通过 HTTP 返回 JSON// src/app/api/health/route.ts import { NextResponse } from next/server; export async function GET() { return NextResponse.json({ status: ok }); }启动开发服务器后可以用浏览器或 curl 验证curl http://localhost:3000/api/health能收到{status:ok}说明 Next.js 应用本身工作正常。7. Prisma 数据模型与数据库迁移tRPC 解决的是接口层类型安全要真正让数据落库还需要数据库表。T3 Stack 默认集成了 Prisma数据库模型定义在prisma/schema.prisma。以 SQLite 为例简单设计一个 Post 模型// prisma/schema.prisma generator client { provider prisma-client-js } datasource db { provider sqlite url env(DATABASE_URL) } model Post { id String id default(cuid()) title String content String createdAt DateTime default(now()) updatedAt DateTime updatedAt }修改完 schema 后在项目根目录执行迁移命令pnpm db:migrate如果 create-t3-app 的模板没有提供db:migrate脚本可以直接使用 npxnpx prisma migrate dev --name init迁移成功后Prisma Client 会自动重新生成。此时数据库里会出现Post表接下来就可以在 tRPC router 里写 CRUD 逻辑。新建src/server/api/routers/post.ts// src/server/api/routers/post.ts import { z } from zod; import { createTRPCRouter, publicProcedure } from ~/server/api/trpc; export const postRouter createTRPCRouter({ create: publicProcedure .input( z.object({ title: z.string().min(1), content: z.string(), }) ) .mutation(async ({ ctx, input }) { return ctx.db.post.create({ data: { title: input.title, content: input.content, }, }); }), getAll: publicProcedure.query(async ({ ctx }) { return ctx.db.post.findMany({ orderBy: { createdAt: desc }, }); }), });然后在根 router 中挂载// src/server/api/root.ts import { createTRPCRouter, publicProcedure } from ~/server/api/trpc; import { postRouter } from ~/server/api/routers/post; export const appRouter createTRPCRouter({ post: postRouter, }); export type AppRouter typeof appRouter;保存文件后开发服务器会自动重新生成类型。前端组件里就可以调用api.post.getAll.useQuery()查询列表调用api.post.create.useMutation()写入数据。这里体现出的优势是数据库表字段、后端入参、前端组件变量三处类型由同一个链路推导出来少了手动对字段名的环节。数据库迁移是整个流程中最容易出问题的一步。常见错误包括.env里DATABASE_URL没配置或格式错误Prisma schema 语法不对迁移命令执行时数据库文件被其他程序占用。排查时先看DATABASE_URL是否指向了正确的file:./db.sqlite路径然后看终端报错信息是连接问题还是 schema 语法问题。8. 全栈联调与功能测试接口和数据库都就绪后可以做一次完整的全栈联调。测试目标很简单前端提交一个 Post刷新后内容还在说明数据确实写库成功。首先在页面中写一个简单的 Post 列表和创建表单组件use client; import { useState } from react; import { api } from ~/trpc/react; export function PostList() { const posts api.post.getAll.useQuery(); const createPost api.post.create.useMutation({ onSuccess: () { void posts.refetch(); }, }); const [title, setTitle] useState(); const [content, setContent] useState(); return ( div input value{title} onChange{(e) setTitle(e.target.value)} placeholder标题 / input value{content} onChange{(e) setContent(e.target.value)} placeholder内容 / button onClick{() { createPost.mutate({ title, content }); }} 创建 Post /button ul {posts.data?.map((post) ( li key{post.id} strong{post.title}/strong : {post.content} /li ))} /ul /div ); }联调测试的预期结果包括页面一打开就显示数据库里已有的 Post 列表输入标题和内容后点击按钮新的 Post 出现在列表顶部刷新页面后新数据不掉。这三个标准都满足说明本地开发链路是通的。如果想要验证批量操作比如快速生成 10 条测试数据可以在项目里写一个脚本。T3 Stack 本身没有内置批量任务队列但脚本方式足够覆盖开发阶段的批量造数需求。先安装 tsxpnpm add -D tsx然后创建scripts/seed.ts// scripts/seed.ts import { PrismaClient } from prisma/client; const prisma new PrismaClient(); async function main() { const posts Array.from({ length: 10 }, (_, i) ({ title: 批量任务 ${i 1}, content: 这是第 ${i 1} 条测试内容, })); const results await Promise.allSettled( posts.map((data) prisma.post.create({ data })) ); const successCount results.filter((r) r.status fulfilled).length; console.log(成功写入 ${successCount} 条); } main().finally(() prisma.$disconnect());运行脚本npx tsx scripts/seed.tsSQLite 对并发写入比较敏感如果Promise.allSettled并发写导致database is locked报错可以把批量逻辑改成串行 for 循环或者减少每次批量写入的条数。生产环境遇到高频写入场景建议直接换 PostgreSQLSQLite 更适合单机、低并发的开发阶段和使用场景。9. 资源占用与性能观察T3 Stack 不涉及 GPU 和显存性能观察主要看 CPU、内存、磁盘和数据库响应时间。开发模式下Next.js 编译是资源开销的大头首次启动会比较慢后续页面访问会快很多这是正常的。你可以打开任务管理器或活动监视器找到 node 进程观察内存占用。不同项目依赖数量不同内存占用会有差异以本机实际为准如果发现内存长期异常上涨再排查是否有循环请求或 Prisma 连接未释放。在 Linux 或 macOS 上可以使用ps命令查看 node 进程ps aux | grep node关注的是内存列和 CPU 列。项目刚启动时 CPU 占用可能较高因为 Next.js 要做模块编译页面编译完成后 CPU 会降下来。如果开发服务器长期高频占用 CPU考虑是不是有无限触发的 tRPC 查询、页面里循环调用了接口或者某个组件刷新频率过高。影响性能的几个关键因素要提前知道。数据库查询是最常见的瓶颈Prisma 查询如果没有限制返回条数数据量大了以后会越来越慢给列表查询加上take参数和排序索引很有必要。tRPC 的输入输出数据量大时序列化和传输时间会增加建议不要把整个大对象直接返回给前端。前端组件如果用客户端组件包裹了太多内容会导致浏览器执行过多 JavaScript尽量把静态部分放到服务端组件里。构建生产包的内存和耗时也要有预期。执行pnpm build时Next.js 会做全量编译内存占用会高于开发模式。如果项目变大可以考虑调整 Next.js 构建缓存配置或者把重型查询改成分页加载。这些优化不是 T3 Stack 专属但在这个全栈架构下很有效。10. 常见问题与排查方法以下表格整理了 T3 Stack 开发过程中的常见问题覆盖初始化、启动、数据库、tRPC、构建等阶段。问题现象可能原因排查方式解决方案create-t3-app 初始化失败npm 网络问题或 Node 版本过低检查终端报错执行node -v升级 Node.js 版本切换 npm 镜像后重试pnpm dev启动后 3000 端口被占用其他进程占用端口看日志是 3000 还是自动跳到 3001让 Next.js 自动选择端口或指定pnpm dev -- -p 3100浏览器访问 localhost 打不开服务未启动或防火墙拦截检查终端日志确认端口监听状态重新启动 dev 服务检查防火墙设置Prisma 迁移失败提示找不到数据库.env中DATABASE_URL未配置或路径错误检查.env文件是否存在参照.env.example配置DATABASE_URLfile:./db.sqlitePrisma 迁移成功但查询报表不存在schema 变更后没有执行生成客户端看node_modules/.prisma是否更新重新执行npx prisma migrate dev或npx prisma generatetRPC 前端调用报类型错误后端 router 未挂载或前后端类型不同步检查root.ts是否挂载新 router保存文件后重启 dev 服务必要时删除.next缓存tRPC 请求返回 500context 或数据库初始化出错查看服务端终端堆栈检查src/server/db.ts确认数据库连接串提交 Git 后环境变量丢失.env被 Git 忽略新环境没配置检查.env.example是否完整复制.env.example为.env并填写真实密钥批量写入 SQLite 报 database is lockedSQLite 并发写限制看报错出现时机改成串行写入降低并发或生产环境换 PostgreSQL生产构建next build内存不足编译时依赖过多查看构建工具输出控制单个页面依赖体积开启代码分割升级机器内存页面请求卡住不返回接口循环调用或数据库查询慢查看浏览器 Network 面板和终端日志加 loading 状态优化查询增加超时与错误处理遇到问题时第一步永远先看终端输出。Next.js 开发模式的报错信息通常会把错误堆栈和对应文件路径直接打在终端里比在浏览器里猜根因高效得多。如果终端没有输出再看浏览器控制台和 Network 面板确认请求是 pending、还是 500、还是根本没发出。11. 最佳实践与使用建议第一第一次运行 T3 Stack 时不要急着写业务代码先把最简链路跑通。项目初始化成功访问默认首页正常api.hello能返回数据然后再引入 Prisma 和数据库。这样每一步都只引入一个变量出问题时定位范围小。第二保持目录职责清晰。数据库模型放到prisma目录tRPC router 放到src/server/api/routers前端页面组件放到src/app全局样式放到src/styles。T3 Stack 已经把这套结构搭好了不要为了“快”把所有逻辑堆到page.tsx里。项目一旦变大目录混乱带来的维护成本会迅速上升。第三环境变量管理要严格。不要把真实密钥直接写进.env后提交到 Git也不要把.env改成 npm 包发出去。项目仓库里维护一个干净.env.example每个新的环境变量都要在示例文件里登记。团队成员拿到项目后复制示例文件并本地填写自己的密钥这是最基本的协作规范。第四数据库迁移需要纳入版本管理。每次修改schema.prisma后生成的迁移文件要提交到 Git这样其他开发者执行pnpm db:migrate才能得到相同结构的表。如果迁移文件丢失数据库结构只能手工对齐容易出线上事故。第五使用 Zod 校验所有外部输入。tRPC 的input()不是可选的装饰而是接口安全的一道防线。哪怕内部项目也要对 title、content 这些字段做长度和类型校验后端不要相信前端传来的任何数据。第六批量任务要加日志和失败重试。上文中的 seed 脚本是简单示例真实生产环境如果要做批量数据导入通常要记录每个任务的成功失败状态、失败原因、重试次数并用队列方式控制并发避免大量请求同时打到数据库。第七上线前做安全检查。如果接口涉及用户资料、文件上传、支付信息等敏感数据必须配置鉴权。T3 Stack 默认带 NextAuth 选项但需要认真配置 session 策略和访问控制不能只停留在“能登录”的状态。发布到公网后建议限制管理后台的访问 IP并定期检查日志。最后保留一套最小可运行模板。我建议在团队里维护一个精简版 T3 Stack 基线项目里面只有认证、数据库连接、健康检查接口和基础布局新业务从这套基线复制而不是让每个开发都重新从交互式的 create-t3-app 开始选择一遍模块。12. 总结与下一步T3 Stack 最值得尝试的地方是类型安全链路。从 React 组件调用 tRPC到后端 router 的参数校验再到 Prisma 数据库模型TypeScript 类型和运行时校验贯穿了整条数据流。对这种开发方式不熟悉的人第一次体验会非常明显因为前端一个字段写错编译器直接告诉你问题在哪而不是等到运行时才发现接口 500。按本文的顺序最先应该验证的就是 project 初始化后访问默认首页然后加一个简单的 hello 查询这个链路能跑通说明环境没有问题后续加数据库和业务路由才有意义。最容易踩的坑基本集中在 Prisma 数据库连接串、tRPC 类型缓存不同步、环境变量缺失这三类遇到报错不要慌先看.env再清一次.next和node_modules/.cache通常能解决大部分开发期问题。接下来可以考虑的方向接入 NextAuth 增加登录和权限控制把 SQLite 切换成 PostgreSQL部署到服务器或 Vercel用 Drizzle 替换 Prisma 体验更轻量的查询方式把 tRPC 的调用方式封装成独立 client供非 React 场景使用。如果团队已经有现成的后端服务也可以只拿 T3 Stack 做前端项目把 tRPC 部分换成纯 HTTP 调用保留 Tailwind 和 Next.js 的开发体验。这篇内容是按“能否跑通、怎么部署、怎么验证、怎么排错”的思路来写的。T3 Stack 本身不是复杂框架但它的组合项多、目录约定细照着上面流程操作一遍比单纯看文档能更快建立整体概念。建议收藏备用下次初始化全栈项目时直接按这个清单走。