t3code:工程化代码生成器,快速生成类型安全的Next.js全栈项目
发布时间:2026/10/27 7:22:02 锦皓数字建站

如果你最近在新项目的初始化阶段反复折腾过大概率能理解我为什么愿意花时间把 t3code 从一堆脚本里捞出来单独维护。t3code 是我整理的一套代码工程化生成器名字里的 T3 沿用了 TypeScript、Tailwind、tRPC 这条技术主线的说法code 则代表它更关注最终落地的代码形态。简单说它用一条 CLI 命令生成一个已经配好目录规范、类型安全链路、样式体系和基础鉴权的 Next.js 全栈项目后续还能按业务模块增量生成 CRUD 代码省掉复制粘贴时最容易出现的低级错误。适合正在做中台、后台工具、内部 SaaS 的团队也适合个人开发者想快速验证思路。先别急着把它当成某个现成脚手架的替身。t3code 解决的重点不在“初始化出来的项目能不能跑”而在于让项目里的代码长成一套可预期的样子业务模块独立、类型定义单一来源、前端调用接口不靠猜。整个工具在我看来是一个低成本的基础设施投入一次后续每个新模块都省心。下面把设计思路、核心实现、实操过程和踩坑记录完整梳理一遍想接入自己团队的人可以直接参考这套做法。1. 为什么从零搭一个工程反而更慢t3code 的设计思路1.1 被低估的初始化成本很多团队开新项目第一反应是create-next-app。跑完命令确实能快速得到一个可运行的基础项目但真正开始写业务时你还是会卡在几类问题上目录结构没有统一有人按页面堆文件有人按组件分目录接口调用路径靠手写字符串前端改一个字段后端完全不报错ESLint、Prettier、Husky 这些基础配置散落在各台电脑上无法保证每个成员提交的代码格式一致。这些成本不是不可逾越但每个项目都重来一遍就非常浪费。我印象最深的是一个后台项目团队前三天几乎没写业务代码全在补环境、调 lint、商量接口数据结构。等到第四天真正开始写接口时发现 Prisma 模型和前端类型已经是两套东西了。这种场景在中小团队里太常见了不是某个人能力不行而是“项目启动”这件事本身缺少一条固定路径。t3code 的第一版需求就是这么来的把每个项目都要做一遍的动作收敛成一个模板同时保留根据场景裁剪的空间。所以它不是一个框架而是一个代码生成器。它的内核是模板和配置不是运行时依赖。业务代码里不会出现 t3code 的 SDK项目跑起来之后它只是一个普普通通的 Next.js TypeScript Prisma tRPC 项目。好处是后续可以随时放弃这套工具已经生成的代码不会绑架你的项目。1.2 从“复制模板”到“参数化生成”早年我也用过复制整个模板仓库的方式先把项目 A 拷过来再全局替换项目名。问题出在替换不干净、隐藏配置没同步、多个项目各自演进后差异越来越大。更麻烦的是模板仓库一旦更新已经复制出去的项目很难自动吸收改动团队里逐渐出现“同一个项目三种写法”的乱象。t3code 的做法是用 JSON 配置文件描述项目特征再通过模板引擎一次性渲染出完整项目。同样的模板传入不同的项目名、包管理器、是否接入 Prisma、是否需要鉴权得到不同的结果而不是生成完再手动删文件。这样做的好处是项目之间只共享“生成逻辑”不共享“物理文件”每个项目仍然可以独立演进。这里的关键是配置项不能太多否则生成器本身就成了一个需要维护的项目。我实际只保留了十几个顶层配置比如项目名、包名、模块列表、数据库类型、认证方案。其余的细节都收敛到固定约定里比如路由必须放在src/app业务组件放在对应模块目录下。约定优先于配置这是 t3code 能保持轻量的原因也是它和那种“通过几十个选项组装项目”的工具最大的区别。2. 核心细节解析目录结构、类型安全与模块拆分要理解 t3code 的价值不能只看命令怎么跑更值得看的是默认生成出来的代码长什么样。很多脚手架只解决“能运行”不解决“好维护”而后面这件事才是团队真正需要的基础设施。2.1 目录结构按 feature 而不是按技术分层大部分 Next.js 项目教程里都会让你把components和hooks分别建一个顶层目录。这种按技术类型分层的结构在小项目里看着干净一旦业务模块变多就会变成“找文件靠翻改完忘记还有关联文件”。t3code 默认生成的是按 feature 组织的目录每个业务模块拥有独立的api、components、schemas、types模块之间尽量不互相依赖。src/ app/ (auth)/... (dashboard)/... modules/ auth/ api/ components/ schemas/ types.ts project/ api/ components/ schemas/ types.ts lib/ db.ts auth.ts query-client.ts shared/ components/ utils/这个结构对绝大多数后台业务足够清晰。app只做路由入口和布局真正的业务实体都放在modules下。shared放跨模块复用的基础件例如按钮、输入框、日期工具但不放任何和业务字段强相关的组件。团队协作时新人找到一个模块只需要看一个目录不用在大杂烩的components里靠记忆力翻找。有人会担心按模块拆分会导致重复代码变多比如两个模块都用到了同一个表格组件。我的处理方式是基础组件下沉到shared业务组件留在模块内。比如“用户表格”和“项目表格”虽然长得像但列和操作逻辑完全不同放一起反而要写大量条件分支。按模块隔离之后改一个模块的操作不会影响另一个模块回归成本明显变小。2.2 类型安全三层联动Schema、ORM、RPCt3code 的另一个核心设计是让一条数据从数据库到表单全程类型可追踪。默认链路是 Zod 定义校验规则Prisma 定义数据库模型tRPC 暴露 API前端 React Query 根据 tRPC router 自动推导出类型。听起来复杂实际理解就是在一处修改字段其他环节马上能感知。// src/modules/member/schemas/create-member.schema.ts import { z } from zod; export const createMemberSchema z.object({ email: z.string().email(), name: z.string().min(1).max(50), role: z.enum([ADMIN, MEMBER]).default(MEMBER), }); export type CreateMemberInput z.infertypeof createMemberSchema;上面这段 schema 既是 API 入参校验又是前端表单的字段规则来源。t3code 在生成模块时会自动把 schema 接入 tRPC router所以前端调用createMember接口时如果没有传email类型检查阶段就会报错根本轮不到运行时才返回 400。这个体验比传统 REST 接口靠文档和 Postman 同步字段要稳得多。对比项传统 REST / RPCt3code 默认链路接口定义手写路由字符串和入参类型router Schema 自动生成数据库字段手动同步到模型和接口Prisma 模型 Zod schema 双写但可检查前端调用手写类型断言或复制类型tRPC 自动推导校验时机运行时才发现缺失类型期 运行时双层兜底这里要强调一点双写并不等于没有成本。t3code 的做法不是消灭双写而是把两处的关联做得足够显眼。生成模块时Prisma model 和 Zod schema 放在同一个模块目录下改动时一眼就能看到对应关系。后续如果调数据库字段顺手同步 schema 就行了而不是像传统项目那样在三个不同层级里手工找。3. 实操过程从零初始化一个 t3code 项目并接入业务模块3.1 环境准备与 CLI 初始化实际操作层面不需要太多前置条件。我用的是 Node 18 和 pnpm因为模板默认生成 pnpm workspace理论上 npm 和 yarn 也能跑但没有 pnpm 的按需安装体验顺畅。输入下面命令npx t3code init my-app命令执行后会依次问几个问题项目名称、包管理器、数据库类型、是否启用 NextAuth 鉴权、初始模块列表。项目名称我推荐用中划线小写例如my-admin避免生成 import 路径时出现大小写不一致的问题。选择完成后CLI 会创建一个my-app目录并安装依赖过程大概一到两分钟取决于网络。初始化后目录里会有一个t3code.config.json文件它是后续增量生成模块的配置中心。比如我已经启动了一个项目现在要加一个文章管理模块不需要重新init直接执行t3code module add即可。这个设计很贴近实际项目节奏——没有谁会在项目初始化时就把所有模块规划好大部分时候是边做边加。第一次使用会有点不习惯的是t3code 默认不会生成src/pages目录而是按 Next.js App Router 的方式生成src/app。App Router 的嵌套布局和 server component 能力很适合后台项目但如果你是从 Pages Router 时代过来的需要先适应布局文件layout.tsx和页面文件page.tsx的分工。3.2 配置数据库并生成第一个业务模块假设我现在要做一个项目成员管理功能。数据库用的 Postgres初始化时已经选择了 Prisma。第一步是把数据库连接串写入.envDATABASE_URLpostgresql://user:passwordlocalhost:5432/myapp然后执行下面的命令同步表结构pnpm prisma db push接着用 t3code 生成成员模块。我习惯先定义字段再让生成器去补全整套代码npx t3code module add member \ --fields email:stringemail \ --fields name:stringmin:1,max:50 \ --fields role:enum:ADMIN,MEMBER这条命令会在src/modules/member下生成 Prisma 模型、Zod schema、tRPC router 和前端 React Query hooks同时把路由自动挂载到app的一级菜单中。生成完看一眼代码会发现比自己手动写完再去补类型要可靠很多。// src/modules/member/api/member-router.ts import { createTRPCRouter, protectedProcedure } from ~/lib/trpc; import { createMemberSchema, listMemberSchema } from ../schemas/member.schema; export const memberRouter createTRPCRouter({ create: protectedProcedure .input(createMemberSchema) .mutation(async ({ ctx, input }) { return ctx.db.member.create({ data: input }); }), list: protectedProcedure .input(listMemberSchema) .query(async ({ ctx, input }) { return ctx.db.member.findMany(); }), });这里有一个细节生成器在 schema 里自动把 email 字段设为唯一的 Prisma 模型字段并在 Zod 里做了 email 格式校验。如果你后续想调整字段类型不要直接改 router而是改 schema 文件后重新执行生成命令或者手动同步 Prisma 模型。t3code 并不会每次都在现有文件上强覆盖这样可以避免误删业务代码但也意味着改完模板规则后需要自己重建已有模块而不是自动迁移。4. 常见问题与排查技巧实录4.1 pnpm 依赖冲突与 peerDependencies 处理用 t3code 生成的模板默认把 Next.js、React、tRPC 的版本锁定在一个组合里按原样跑一般没问题。但很多人在使用一段时间后手动升级某个依赖就会开始出现版本冲突。我在实践中遇到最多的是 React 18 与某个 UI 库的 peerDependencies 不兼容或者 tRPC 的 server 端和 client 端版本不一致。tRPC 这个生态比较特殊trpc/server和trpc/client必须保持版本一致否则类型推导会直接崩掉。像 NextAuth 和 Prisma 这层还相对宽容而 tRPC 只要差一个小版本inferRouterOutputs那一类工具类型就会失效。这时候最好的做法不是单独升级某一个包而是整组升级 tRPC 相关依赖。如果项目里还有其他库和服务端版本锁死可以在 pnpm 的 overrides 里强制统一// package.json { pnpm: { overrides: { trpc/server: ^10.45.0, trpc/client: ^10.45.0 } } }我通常会先执行pnpm why trpc/server看实际安装版本再决定要不要 override而不是盲加。依赖冲突这个东西越早处理越省钱拖到业务代码多起来再升级成本会翻倍。4.2 tRPC 类型不同步与路由 404 的排查顺序新人在接入模块时最容易遇到两个现象前端调用某个 procedure 时类型报错以及运行时接口 404。前者八成是类型没有更新。tRPC 的类型是根据 router 结构推导的如果你改了 router 里的 method但 Next.js dev server 没有完成重新编译或者多包项目里 client 端引用的是旧入口就会一直看到类型报错。可以先停掉项目重新执行pnpm dev如果还不行就检查t3code.config.json是否把新模块正确注册到 router 聚合文件里。路由 404 的情况我一般按这个顺序排查先看src/app目录下路由文件位置是否和模块名一致。再看 tRPC root router 是否挂载了新模块。用浏览器 network 面板看实际请求路径。最后确认 React Query 的 key 和 tRPC 路径是否被手写过。React Query 的 key 和 tRPC 的路径是自动生成的手动改过 key 才会对不上。另外还有一个容易被忽略的小坑App Router 的 server component 里直接调用 tRPC procedure 时如果传入了非序列化对象会触发运行时错误。虽然和 404 无关但排查思路是一样的——先怀疑约定被破坏再怀疑框架出问题。现象优先排查点常用处理类型报错tRPC server/client 版本不一致整组升级或 overrides类型不更新dev server 未重新编译重启 dev 进程接口 404root router 未挂载检查模块生成配置请求路径错误手写了 query key交给自动生成的 hook4.3 让模板长期好用的三个维护习惯t3code 这类工具最大的敌人是模板和业务代码漂移。我见过不少团队刚开始模板很统一三个月后每个模块写法都不一样。三个习惯能减少这个问题第一约定式生成先于手写。能用 t3code 生成的模块就不要手抄至少保证初始代码结构一致。人工介入越多风格分裂越快。第二模板变更走版本化。不要直接在生成项目里改一个文件然后到处复制把变更提交回 t3code 的模板仓库再发布一个新版本。否则模板更新和项目实际代码会越走越远。第三定期抽查。我在团队里每隔一个迭代会挑一个模块做代码评审重点看 schema 是否在 Prisma 和 Zod 两边同步router 里是否混入了别的模块的接口。这是最简单也最容易被忽略的维护动作。以上三个习惯加上工具本身的约定基本能让项目在半年内保持“不看目录也知道文件在哪”的状态。如果你正好也在为新项目工程化发愁不妨基于这套思路先把模板搭起来哪怕不用 t3code 这个名字也可以从模块化目录和类型联动这两点入手。模板带来的一致性收益远比多敲几条初始化命令更大。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。