news 2026/10/27 6:50:38

全栈TypeScript实践:t3code项目如何用tRPC+Prisma打通前后端类型安全

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
全栈TypeScript实践:t3code项目如何用tRPC+Prisma打通前后端类型安全

如果项目从一开始,后端每个接口的类型定义就能直接"长"到前端页面上,联调时不用翻文档对字段,数据库字段改了编译器立刻告诉你哪儿挂掉了,你会不会觉得省掉一大半的心累?我去年用整套TypeScript工具链把一个真实业务从零做起来的时候,非常想验证这条路到底走不走得通,于是有了 t3code 这个代号项目。

t3code 不是一个成熟的开源框架,也不是某个公司的内部系统,它是我针对 T3 Stack(TypeScript、Tailwind CSS、tRPC、Next.js 组合)做的一次系统化落地实验。整个项目从脚手架、数据建模、权限链路、业务模块到部署上线全部走了一遍,过程中记录的选型理由、目录规划、踩坑链路和性能优化,就是这篇总结的主要内容。如果你正准备上手全栈 TypeScript 项目,或者在纠结自己要不要上 tRPC、Prisma 这套技术组合,这篇内容应该能帮你省不少试错时间。

我先把话说清楚:这篇不是吹某个技术栈多神,而是把一个真实的项目拆开给你看,哪里顺、哪里坑、哪里后期想改,都如实交代。

1. t3code的定位:一套以"类型安全"为核心的全栈项目骨架

1.1 从"前后端各写各的"到"一份类型走到底"

大多数团队做 Web 项目,协作链路是这样的:后端定义接口,要么手写文档,要么用 Swagger/OpenAPI 生成文档;前端拿到文档,在项目里手写对应的类型定义或请求函数。这个流程最大的问题在于:类型定义和文档本质上是"第二份代码",它一旦和真实实现脱节,就会产生信任危机。我做过一个后台系统,后端改了某个字段的枚举值,文档没更新,前端按旧枚举做下拉框,上线后数据全部显示为空,排查了一整天,最后发现是文档过期了。

t3code 想解决的问题就是这个"第二份代码"的问题。整个项目从前到后只用一份 TypeScript 类型体系:Prisma 负责把数据库表变成类型,tRPC 负责把服务端路由的入参出参类型直接投射到客户端,Next.js 负责把数据传到组件里。前端调用后端接口,不需要有人写类型,因为类型是从服务端"长"出来的。

通过 t3code 的落地,我最大的感受是:类型安全的价值不在于"写代码时有自动补全",而在于"改动时编译器能在运行前告诉你哪里会挂"。数据库字段改了,用到的所有查询和组件会同时报错;接口入参加了必填项,前端调用处会立刻标红。这种安全感是传统接口文档没法给的。

1.2 这个项目适合谁参考

虽然 t3code 是一个代号比较"极客"的项目,但它参考价值并不局限在技术极客群体。

  • 如果你是一个前端工程师,想试试自己能不能独立把后端业务模块做出来,T3 Stack 是入门曲线相对平滑的组合。
  • 如果你是一个小团队的技术负责人,正在选型下一代内部后台系统或业务中台的技术栈,t3code 可以当一份"选型验证报告"看。
  • 如果你只是在学 Next.js,但一直觉得"前后端类型不互通"很别扭,想看看全栈 TypeScript 到底能走到什么程度,这篇也值得读。

当然,t3code 不适用于所有场景,这一点我放到最后专门讲。先来看我当时选型时是怎么想的。

2. 技术选型定案:为什么最终选了tRPC而不是REST或GraphQL

2.1 REST和GraphQL在"类型打通"上的天然短板

先别急着看结论,我把当时真正纠结过的问题交代清楚。

REST 是绝大多数团队的第一直觉。它成熟、通用、任何客户端都能消费。但如果你追求"一份类型走到底",REST 的问题很明显:接口是资源导向的,不是程序导向的。你得在 OpenAPI 文档里定义完整的 schema,再用 openapi-typescript 这类工具生成类型,然后还得保证生成链路在每次接口变更后都跑一遍。这套方案不是不行,只是链路长,而且一旦有团队成员绕过规范直接手写类型,类型断层就产生了。

GraphQL 是另一个常被提到的方案。它天生有 schema,类型也比较规范,配合 GraphQL Code Generator 确实能自动生成前端类型。但问题在于,GraphQL 引入了太多额外概念:resolver、fragment、缓存归一化、指令……对于一个内部系统来说,这层抽象带来的心智负担明显偏高。我见过不少团队 GraphQL 服务端写得比 REST 还随意,最终还是靠人工 review 保证质量。

2.2 tRPC的核心逻辑:让TypeScript本身成为协议

tRPC 的思路非常"反直觉":它不定义协议,而是把函数调用当作 API。服务端写一个 router:

// src/server/api/routers/post.ts import { z } from "zod"; import { protectedProcedure, createTRPCRouter } from "../trpc"; export const postRouter = createTRPCRouter({ list: protectedProcedure .input( z.object({ limit: z.number().min(1).max(50).default(10), cursor: z.string().cuid().optional(), }) ) .query(async ({ ctx }) => { const posts = await ctx.db.post.findMany({ where: { authorId: ctx.session.user.id }, take: 10, skip: cursor ? 1 : 0, cursor: cursor ? { id: cursor } : undefined, orderBy: { createdAt: "desc" }, }); return { items: posts, nextCursor: posts.at(-1)?.id ?? null }; }), });

前端调用的时候,只要从src/trpc/react.ts引入封装好的客户端:

const { data } = api.post.list.useQuery({ limit: 10 });

注意看,这里没有导入任何后端类型,也没有定义任何请求函数。data的类型是 TypeScript 根据服务端 router 的返回类型自动推导出来的。如果我改了服务端返回结构,比如把published字段改成了status,前端使用data.items[0].published的代码会立刻报错。

用一个日常类比:REST/GraphQL 像是请一个翻译官,双方都要按翻译官定的格式说话,格式文档一旦更新不及时就会沟通错乱;tRPC 则像是两个人直接用同一种母语对话,根本不需要翻译官,想表达什么直接说,彼此天然能听懂。

2.3 tRPC的边界:它只适合"前后端一体的TS应用"

这里必须说清楚 tRPC 的限制,不然你后面会踩坑。tRPC 的客户端必须是 TypeScript 项目——它靠 TS 类型做协议,如果将来要服务一个原生小程序、一个纯 Python 后端、或者任何非 TS 客户端,这条路就断了。这是 t3code 选型时反复权衡过的代价:项目定位是"全栈 TypeScript 应用",而非"开放 API 平台"。

配套选型也围绕这条主线展开:

技术组件在t3code中的角色选型理由
TypeScript全栈语言类型体系贯穿所有层面
Next.js应用框架React服务端组件、路由、SSR/SSG开箱即用
tRPCAPI层服务端类型直接推导到前端
PrismaORM层schema即类型,数据库模型和TS类型同源
Tailwind CSS样式层组合式样式,适合快速迭代后台界面
NextAuth.js认证层与Prisma、tRPC集成度高,session类型可自定义

Prisma 在这套选型里承担了"地基"的角色:数据库表结构定义在 schema 里,跑一次prisma generate,ctx.db上的模型方法和查询结果就全部有了精确类型。配合 tRPC 的输出推导,从数据库字段到前端组件 prop 的类型链路全程不落地成任何中间文档。

3. 从零搭建t3code:目录结构、数据模型与权限链路实战

3.1 用脚手架还是手动搭:我最终选了create-t3-app

搭建 t3code 第一步就遇到了选择:是自己手工配置 Next.js + tRPC + Prisma,还是直接用脚手架?我最终用了create-t3-app,原因很现实:这套组合的手工接线非常繁琐,而且容易出错。尤其是 tRPC 和 Next.js 的 App Router 配合时需要处理服务端客户端上下文隔离、React Query 边界等问题,一个文件放错位置,类型推导就会全线崩坏。

create-t3-app运行时会让你勾选需要的模块,我勾选了 Next.js App Router、tRPC、Prisma、NextAuth、Tailwind,后续它会生成一套相对干净的目录结构。注意它生成的代码版本比较保守,你最好在项目初始化后立刻把依赖升级到当前稳定版,我实测下来能避免不少已知 bug。

项目主体目录结构如下:

src/ ├── app/ # Next.js App Router页面 │ ├── api/auth/[...nextauth]/ │ ├── layout.tsx │ └── page.tsx ├── server/ │ ├── api/ │ │ ├── root.ts # 汇总所有子router │ │ ├── trpc.ts # createTRPCRouter、protectedProcedure │ │ └── routers/ # 按业务域拆分 │ ├── auth.ts # NextAuth配置 │ └── db.ts # Prisma Client实例 ├── trpc/ │ ├── react.ts # 客户端React hooks封装 │ └── server.ts # 服务端调用tRPC的封装 └── types/ # 全局类型补充

这个结构的核心原则是:所有涉及数据库、认证、服务的代码全部放在src/server下,绝不直接暴露到客户端组件里。这样既能保证安全,也让类型边界很清晰。

3.2 数据模型与第一个业务模块:做一个简单的文章列表

我拿"用户-文章"这个经典模型来验证 t3code 的整个数据流。Prisma 的 schema 定义如下:

// prisma/schema.prisma generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(cuid()) name String? email String? @unique emailVerified DateTime? image String? posts Post[] accounts Account[] sessions Session[] } 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 后先跑npx prisma migrate dev --name init建表,再跑npx prisma generate生成类型。Prisma Client 会在src/server/db.ts里挂载:

import { PrismaClient } from "@prisma/client"; const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }; export const db = globalForPrisma.prisma ?? new PrismaClient(); if (process.env.NODE_ENV !== "production") { globalForPrisma.prisma = db; }

这一段在开发环境复用同一个 PrismaClient 实例,避免每次热更新都新建数据库连接,连接数被打满。

3.3 权限链路:从登录到受保护查询

权限是后台系统绕不开的环节。t3code 的权限链路分两层:

第一层,NextAuth 负责登录和 session。我在配置里用 JWT 模式,并做了 Prisma Adapter 集成,这样用户数据落在自己的数据库,而不是外部 OAuth 服务里。

第二层,tRPC 的protectedProcedure负责接口级的权限校验。它的本质是在普通 procedure 外层套了一个中间件,在进入业务逻辑之前检查ctx.session?.user是否存在:

// src/server/api/trpc.ts export const protectedProcedure = t.procedure.use(({ ctx, next }) => { if (!ctx.session?.user) { throw new UNAUTHORIZED(); } return next({ ctx: { session: { ...ctx.session, user: ctx.session.user }, }, }); });

前端用这套权限链路时有一个明显的收益:登录状态到接口鉴权之间的状态是闭环的。用户退出登录后,session 失效,受保护的 tRPC query 会直接返回 401 错误,React Query 会收到错误状态,前端页面可以统一做跳转登录的逻辑,不用在几十个页面里各写一遍判断。

4. 踩坑实录:t3code开发中三个典型问题的完整排查链路

如果只看上面的搭建过程,你会觉得 t3code 这套组合很丝滑。实际开发过程中,我踩过的坑一点不少,而且很多问题查遍搜索引擎都没有直接答案。下面三个坑从头到尾的排查思路,我觉得比结论本身更有参考价值。

4.1 tRPC前端类型不刷新与"类型实例化过深"报错

现象:后端的 router 返回结构明明改了,前端api.post.list.useQuery()拿到的data类型却没有变化;有时还报一行特别诡异的错误——Type instantiation is excessively deep and possibly infinite。

排查过程是这样展开的:

第一步,确定"类型不刷新"是不是工程问题。我先把 dev server 完全停掉,重新跑pnpm dev。结果依旧。说明不是热更新缓存的问题。

第二步,手动跑pnpm tsc --noEmit。这一招极其重要——浏览器或 dev server 的报错往往被吞掉,只有 tsc 会给出最真实的类型错误。结果它报了一堆路径相关的错误,指向src/trpc/server.ts里的类型引用。问题根源是 Next.js 的 tsconfig 里有个paths别名,脚手架生成的是"@/*"指向根目录,但部分深层组件用了相对路径,导致某些类型实例被重复解析,出现深层推断。

解决方法是把 tsconfig 的paths改成明确的别名并统一所有 import:

{ "compilerOptions": { "paths": { "@/*": ["./src/*"], "@server/*": ["./src/server/*"], "@trpc/*": ["./src/trpc/*"] } } }

改完重新加载,类型刷新立刻恢复正常。经验:tRPC 类型链路对 import 路径非常敏感,项目早期就统一用别名,别一会儿相对路径一会儿绝对路径。

第三步,"类型实例化过深"的处理。这个错误通常出现在 router 嵌套层级太深或者.input()里用了过于复杂的 Zod 类型。我的 router 里有三层嵌套,且 input 对象里有十几个字段,于是触发了 TypeScript 的递归深度上限。解法是给 Zod schema 单独起一个类型别名,并且把复杂的 input 拆成独立类型:

const listInput = z.object({ limit: z.number().min(1).max(50).default(10), cursor: z.string().cuid().optional(), }); export type PostListInput = z.infer<typeof listInput>;

这样 TypeScript 在进行类型计算时引用的是具体类型名,而不是每次重新展开结构。

4.2 Prisma shadow database权限引起的迁移失败

第二坑出现在数据库迁移阶段。我在本地跑npx prisma migrate dev --name add_published_flag,结果直接报错:

Error: P3014 Prisma Migrate could not create the shadow database. Please ensure the database user has permission to create databases.

这里要先解释什么是 shadow database。prisma migrate dev需要创建一个临时的影子数据库,用来"预演"迁移过程、生成准确的 diff。报错说明当前连接串指向的数据库用户没有 CREATE DATABASE 权限。

我的排查链路:

  1. 先检查连接串指向的是不是自己以为的库。注意很多云数据库(比如 Neon、Supabase)给的是带连接池的主连接串,直接拿它跑migrate dev时可能用了不支持的连接模式。
  2. 然后检查当前用户在 PostgreSQL 里的角色权限:
SELECT rolname, rolcreatedb FROM pg_roles WHERE rolname = current_user;

发现rolcreatedb是false。 3. 此时有几个方向:给用户加 CREATEDB 权限,或者让 migrate 用另一个有权限的库。最稳的做法是手动创建独立影子库,然后在.env里新增一个SHADOW_DATABASE_URL:

DATABASE_URL="postgresql://user:pass@host:5432/main_db" SHADOW_DATABASE_URL="postgresql://user:pass@host:5432/main_db_shadow"

注意这个影子库不需要跑任何表结构,migrate dev会在里面自动建表、对比、然后清空。给它和主库相同的账号权限即可。

还有一个容易被忽略的坑:如果你在云数据库控制台手动建影子库,连接串必须用直连地址,别用 PgBouncer 的连接池地址,因为连接池模式不支持CREATE DATABASE。我在这里多耗了半小时。

4.3 NextAuth会话里取不到user.id

第三个坑非常典型:登录成功之后,protectedProcedure 里ctx.session.user.id的类型是string,但实际运行时是undefined。

原因要拆两层看。NextAuth 的默认 Session 类型里,user只有name、email、image,没有id。虽然数据库 User 表有id字段,但 JWT 模式下,这个 id 并没有被放进 token 和 session 里。

我需要做两件事。

第一件,类型声明补充。在src/types/next-auth.d.ts里用 TypeScript 的 module augmentation 扩展现有类型:

import { type DefaultSession } from "next-auth"; declare module "next-auth" { interface Session { user: { id: string; } & DefaultSession["user"]; } } declare module "next-auth/jwt" { interface JWT { id: string; } }

第二件,在 NextAuth 配置里加回调,把数据库用户的 id 写进 token,再写到 session:

// src/server/auth.ts callbacks: { async jwt({ token, user }) { if (user) { token.id = user.id; } return token; }, async session({ session, token }) { if (session.user) { session.user.id = token.id as string; } return session; }, }

排查这类问题最好的方式不是盲改代码,而是先在回调里console.log看 token 和 session 的实际结构。我后来发现一个规律:NextAuth 类型定义更新滞后于运行时结构是常态,遇到 session 拿不到自定义字段,第一反应应该是看回调里到底有没有,而不是改业务代码。

这个坑恰恰说明 t3code 这套组合虽然把"类型安全"贯彻得很深,但有些框架自带的类型默认值仍然需要手动打通,它依赖的是工程习惯,不是魔法。

5. 性能与体验优化:t3code中的查询、缓存和渲染细节

类型安全解决的是"对不对"的问题,性能解决的是"快不快"的问题。t3code 做到能稳定运行后,我把精力转向了优化。这里分享三个最关键的优化角度。

5.1 React Query缓存参数不能全用默认值

tRPC 的客户端底层就是 React Query,这带来一个好处:查询结果自带缓存、重试、状态管理,不需要再写 Redux 或 Zustand 去存服务端数据。但默认配置并不适合所有场景。

我用api.post.list.useQuery(undefined, { staleTime: 60_000 })把文章列表的 staleTime 调到了 60 秒。这样用户在列表和详情页之间反复跳转时,60 秒内不会重复请求同一个接口,体感上顺滑很多。

但有一个地方必须改回默认:涉及用户操作后马上要刷新数据的场景。比如发表一篇文章成功后,你需要让列表查询失效:

const utils = api.useUtils(); await utils.post.list.invalidate();

invalidate会让 React Query 把对应 key 的缓存标记为过期,下次渲染时重新请求。经验是:把需要频繁更新的数据(如文章列表)和基本不变的数据(如用户资料、静态配置)分开设计 staleTime,前者短,后者长。

5.2 Prisma查询要抠字段,include要克制

后端性能问题大部分出在数据库查询上。t3code 里我最常用的三条经验:

  • .select()一定要写。很多人一上来findMany()不写 select,把整行所有字段全部查出,哪怕前端只展示标题和作者名。表字段一多,响应体变大,序列化时间也变长。用select只取需要的字段,代码意图也更清晰。

  • .include()非常容易演变成 N+1 查询。比如文章列表接口里,如果每篇文章都要 include 作者信息,Prisma 会发一条文章查询加每条文章一条作者查询。列表有 20 条文章,就是 21 次数据库往返。优化方案是列表接口只查文章,作者信息通过批量 IN 查询一次性取回,或者干脆在前端缓存里做关联。

  • 分页用 cursor 而不是 skip。数据量大了以后,skip翻页越翻越慢,因为数据库要跳过越来越多的行。cursor游标翻页基于索引定位,稳定高效。这正是前面 router 里用cursor + take的原因。

5.3 渲染策略:能服务端别客户端,能静态别动态

Next.js App Router 下最容易犯的错误是:为了图省事,在组件顶部写"use client",结果整个页面变成客户端渲染,首屏白屏时间变长、SEO 也受影响。

t3code 的做法是尽量把页面主体留在服务端组件里,只有需要响应用户交互的按钮、表单、输入框才独立为客户端组件。服务端组件里可以直接用api.post.list的服务端调用方式拿数据,不需要经过 HTTP:

import { api } from "~/trpc/server"; export default async function PostsPage() { const posts = await api.post.list({ limit: 10 }); return <PostList posts={posts} />; }

这里要注意:在服务端组件里调用 tRPC,实质是直接执行同一个 Node 进程里的函数,不会走网络请求。相比把数据传到客户端组件里再由前端发起请求,少了一圈往返,冷启动和首屏数据加载都快很多。

如果页面内容不依赖登录态,还可以用 Next.js 的静态生成能力,构建时就把页面输出,速度进一步提升。t3code 里访问量大的公开展示页都用了这种策略。

6. 部署链路:从本地到云端的最后一公里

本地能跑通,不叫完成;部署到线上,才是项目真正开始接受考验的时刻。t3code 部署到 Vercel 的过程中也踩了不少坑,主要集中在这三块:数据库连接、环境变量、上线前自动检查。

6.1 云数据库和托管平台的搭配

我用的是 Vercel + Neon(云 Postgres)。Vercel 的 Serverless Function 无状态、冷启动多,数据库连接不能像传统服务器那样保持一个长连接。Prisma 这种连接式客户端在 Serverless 环境里要配置连接池。

具体到 Neon,它提供了两个连接串:一个是直连,另一个是带 PgBouncer 的池化地址。Serverless 环境必须用池化地址,否则并发一高就会把数据库连接数打满。在 Prisma 的连接串里我加了?pgbouncer=true&connection_limit=1,表示每个函数实例只维持一条连接,通过外部连接池做复用。

还有一个之前提到的坑的延续:prisma migrate dev这类开发命令不能在 Vercel 上跑,生产环境应该用:

npx prisma migrate deploy

它会按顺序执行 migration 文件,不会去创建 shadow database,非常适合 CI/CD 流程。

6.2 环境变量与密钥管理:安全是部署第一课

t3code 的.env至少包含这几个变量:

DATABASE_URL="postgresql://..." SHADOW_DATABASE_URL="postgresql://..." NEXTAUTH_SECRET="生成的随机密钥" NEXTAUTH_URL="https://你的域名"

NEXTAUTH_SECRET一定要用足够随机的值,生成方式:

openssl rand -base64 32

而且要保证在 Vercel 后台设置的NEXTAUTH_SECRET和本地一致。我遇到过部署后一直跳登录死循环的问题,排查半天才发现是 Vercel 环境里的 Secret 和本地不同,JWT 签名验证失败,session 永远无效。

还有一个小技巧:在.env.example里把这些变量的名字和注释写好,团队成员拉仓库后只需要cp .env.example .env再填实际值,不会漏配。

6.3 上线前自动检查:用CI拦住错误

t3code 的 CI 我只做了三件事,但都极其关键:

- run: pnpm install - run: pnpm lint - run: pnpm tsc --noEmit

顺序有讲究:先 lint 跑代码规范,再 tsc 做全量类型检查,最后才轮到 build。很多人以为next build已经包含了类型检查,但实际上 Next.js 在 build 时的类型检查并不覆盖所有边界,尤其 tRPC 的类型推导链路很容易在 build 阶段被跳过。显式加一步tsc --noEmit后,任何类型断层都会在合并到主分支前被发现。

我在项目里还加了一个pnpm check:types的脚本,本地提交前手动跑一遍,配合 CI 双保险。

7. t3code复盘:这套技术栈的收益、边线和后续建议

7.1 做完了之后我得到了什么

复盘 t3code 这个项目,我最真实的感受是:类型安全不是一个"锦上添花"的开发体验,而是一个能直接降低维护成本的基础设施。

做一个业务迭代时,我不需要先想"后端接口有没有变、文档有没有更新、前端类型要不要同步改"——类型是自动同步的,编译不过就说明有地方没改完。这种确定性在项目规模变大后尤其珍贵。t3code 做到后期,加一个新业务模块的工作流基本变成:改 Prisma schema → 写 tRPC router → 前端直接调用。三个环节之间几乎没有"翻译"成本。

另外,这套技术栈让一个人做全栈的可行性高了不少。传统模式下,一个人要同时维护 Node 后端、API 文档、前端类型定义,容易顾此失彼;而 t3code 这类全栈 TS 工程把注意力集中到了业务本身。

7.2 什么时候别用这套技术栈

这里我给自己泼点冷水,也给你提个醒。

t3code 这套组合不适合以下场景:

  • 需要对外提供开放 API 的场景。比如你要给第三方开发者提供 REST 或 GraphQL 接口,tRPC 的类型协议没法暴露给外部非 TS 客户端,前端 TS 项目也要求主动集成 tRPC 客户端,第三方很难直接调你。
  • 多语言微服务架构。如果后端一部分用 Go、一部分用 Python,tRPC 的"纯 TypeScript"前提就不存在了,强行用它反而会造成语言孤岛。
  • 团队对 TypeScript 掌握不深。类型推导虽然能省事,但也意味着你写代码时要理解泛型、条件类型、inference 这些概念。如果团队全是写惯了 JavaScript 的同学,tRPC 出错的排查成本会比 REST 更高。

技术选型没有银弹,t3code 的边界恰恰是它成立的前提:只有当你决定"全栈 TypeScript"时,类型安全链的路才算走得通。

7.3 给想走同样路线的人几条建议

最后分享几个基于 t3code 实践的建议:

第一,不要拿"练习项目"验证技术栈,拿一个真实业务。t3code 之所以能沉淀出这么多内容,就是因为它有真实的用户模型、真实的权限需求、真实的部署痛点。只有真实业务才会逼你处理分页、鉴权、环境变量、CI 等问题。

第二,把 Prisma schema 设计放在最前面。数据库模型是整套类型链路的源头,字段命名是否统一、关系是否清晰,直接影响后面所有 tRPC router 和前端组件的代码质量。t3code 里我后来为改一个模型命名,连带改了十几个文件,这都是前期设计没做好的代价。

第三,务必养成"改完 router 立刻跑一遍类型检查"的习惯。我会在编辑器里开一个终端专门跑tsc --noEmit,每次保存后端代码后手动看一眼有没有类型错误。这个小习惯帮我抓到了非常多"运行时才爆炸"的问题。

我个人在实际操作中还有一个体会:t3code 这类全栈 TS 项目上线后,迭代速度会越来越快,因为你在前面投入的类型基础设施会在后面持续产生复利。每次改动时编译器替你排查掉的那批错误,就是它最实在的回报。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 6:50:38

从零构建生产级Python CLI Agent框架

1. 项目概述&#xff1a;一个被误读的CLI工具&#xff0c;其实它根本不是“Agent-Reach”最近在GitHub上搜“Agent-Reach”&#xff0c;页面一刷出来&#xff0c;满屏都是带cli、python、MIT License标签的仓库&#xff0c;还有大量用户在问“怎么安装”“打不开怎么办”“是不…

作者头像 李华
网站建设 2026/10/27 6:50:37

物料齐套高效模型:从BOM展开到缺料预警的工程落地

物料齐套这四个字&#xff0c;在制造现场的分量比很多宏大词汇都重。计划员在群里发一句“明天这条线能不能开”&#xff0c;仓库回“还差两个料”&#xff0c;采购回“在途”&#xff0c;品质回“在检”&#xff0c;最后产线停在那儿&#xff0c;一天几十万的产值就在等待里蒸…

作者头像 李华
网站建设 2026/10/27 6:49:37

Unity高并发游戏开发:500人在线贪吃蛇架构解析

1. 项目概述&#xff1a;高并发IO游戏开发挑战去年接手Snake Warz项目时&#xff0c;我们团队面临一个经典难题&#xff1a;如何在Unity框架下实现支持500玩家实时对战的贪吃蛇游戏。传统单机版贪吃蛇只需要处理本地输入&#xff0c;而网络版要解决三个核心问题&#xff1a;高频…

作者头像 李华
网站建设 2026/10/27 6:49:11

Composer 2环境反馈机制:AI编程效率提升的关键

1. Composer 2 环境反馈机制深度解析作为一名长期跟踪AI编程工具演进的开发者&#xff0c;我不得不指出当前行业存在的一个严重认知误区&#xff1a;过度关注模型参数量而忽视环境反馈的价值。Composer 2的出现彻底颠覆了传统评判标准&#xff0c;其环境反馈机制&#xff08;En…

作者头像 李华
网站建设 2026/10/27 6:49:09

1688商品详情API实战指南:稳定获取结构化数据的工程方法

1. 为什么“商品详情自动化”不是一句空话&#xff0c;而是1688生态里最实在的效率杠杆最近帮某做小家电批发的团队重构他们的选品流程&#xff0c;他们原来每天要手动打开300个1688商品链接&#xff0c;复制标题、价格、SKU参数、主图URL、服务承诺这些字段&#xff0c;再粘贴…

作者头像 李华
网站建设 2026/10/27 6:48:18

校园请假管理信息系统全流程数字化方案:从需求分析到验收答辩

简介&#xff1a;校园请假管理信息系统是一套面向学校场景的管理信息系统实践案例&#xff0c;适合学习管理信息系统的学生、课程设计开发者及需要搭建类似平台的管理人员参考。系统围绕学生请假申请、教师审批、数据统计等核心流程展开&#xff0c;涵盖用户界面、数据库管理、…

作者头像 李华