news 2026/10/26 23:32:58

用T3 Stack + tRPC打造类型安全的全栈项目:从选型到部署全记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用T3 Stack + tRPC打造类型安全的全栈项目:从选型到部署全记录

最近我一直在折腾一个叫 t3code 的个人全栈项目,说白了就是用 T3 Stack 从零搭一套带数据库、登录、CRUD 的完整应用。折腾完最大的感受是:以前写接口要在后端定义一套、前端再手工造一遍类型,被 tRPC 的端到端类型安全打破之后,整个开发节奏完全不一样了。这篇文章就从 t3code 的选型讲起,把初始化、tRPC 接口、Prisma 数据层、部署排查这些环节摊开来说,每一步都可以直接抄作业。

如果你正准备做一个中小型全栈项目,或者被前后端类型不一致、接口文档过期这类事烦过,这篇应该会对你有用。我尽量把“为什么这么选”“踩过哪些坑”也讲清楚,而不是只丢一堆命令和代码。

1. t3code 到底是什么:T3 Stack 全栈方案的价值拆解

1.1 T3 Stack 到底由哪些技术组成

T3 Stack 不是一个框架,而是一套组合拳。它把几个“T”开头的技术绑在一起,分别是 TypeScript、Tailwind CSS、tRPC,再加上 Next.js、Prisma、NextAuth 这些配套。t3code 这个项目名,其实就是这套组合在我这里的实践代号。

先列一张表,把这几个成员的角色说清楚:

技术在 t3code 里扮演的角色一句话概括
Next.js应用框架负责页面渲染、路由、API 处理
TypeScript语言底座让所有代码都有静态类型
Tailwind CSS样式方案用原子类快速搭 UI
tRPCAPI 层前端像调用本地函数一样调用后端
Prisma数据层类型安全的数据库 ORM
NextAuth鉴权体系登录、Session、OAuth 一套带走

单独拿出来,每一项你可能都听过。真正值钱的是组合后的效果:从数据库字段到 tRPC 接口,再到前端组件里的数据对象,类型信息全程贯通。通俗点说,就像餐厅菜单和后厨用的是同一份食材清单,客人点单时报错,后厨还没动手就已经知道菜做不了。

1.2 为什么选择这套组合:和传统全栈方案的对比

传统方案里,最常见的是 React/Vue 前端 + Express/NestJS 后端 + REST API。这套路本身没问题,但维护成本会随着项目变大而快速上升。后端加一个字段,前端要手动补类型;后端改一个接口路径,前端要全局搜索替换;接口文档稍微滞后,前后端就各写各的,联调时全靠浏览器 Network 面板猜。

t3code 用 tRPC 把中间层直接省略了。后端定义一个 router,前端立刻获得对应的 hooks,参数类型、返回值类型全部自动推导。我做过一个对比,同样实现“文章列表 + 创建文章”两个接口,传统方案要写后端路由、DTO 定义、前端 API 封装、类型声明四份代码,T3 方案只需要后端写一份业务逻辑。

对比维度传统 REST + 手写类型t3code(T3 Stack)
接口定义路径 + DTO 靠文档维护router 定义后类型自动流入前端
参数校验Joi/class-validator 另写一套Zod 一处声明,前后端共用
客户端调用手写 fetch + 类型断言useQuery/useMutation 自动推导
类型一致性容易漂移,靠联调发现编译期强制约束,改错立刻爆红
接口文档需要额外维护对中小项目基本不需要

当然,这不是说 REST 没用了。t3code 这种方案适合内部系统、中后台、MVP,因为接口只给自己人用,类型安全的收益最大。如果将来要把 API 开放给第三方,或者需要和移动端、外部服务对接,REST/GraphQL 依然是更通用的选择。我自己的原则是:内部端到端用 tRPC,对外暴露用 REST,两者可以共存,不冲突。

1.3 哪些项目最适合拿这套方案开刀

先说实话,t3code 不是银弹。我总结了几类非常合适和不太合适的场景。

合适的场景:

  • 团队内部工具,比如运营后台、数据管理平台、内部工单系统
  • 需要快速验证业务的 MVP 项目,一个月内要上线
  • CRUD 密集的 CMS、博客、笔记类产品
  • 个人全栈项目,一个人同时写前后端,最吃这套类型红利
  • 想系统学习 TypeScript 全栈的开发者,这套组合可以把类型思维一次性打通

不太合适的场景:

  • 大型微服务架构,服务之间边界复杂,tRPC 的优势会被冲淡
  • 团队里的前端或后端有一方不用 TypeScript,类型贯通无从谈起
  • 需要把 API 开放给第三方生态,公开接口还是老老实实用 REST
  • 历史项目里已经有大量 REST 接口,硬迁 T3 成本很高,不如渐进式改造

我见过一些团队为了赶时髦强行上 T3,结果前端不会 tRPC、后端不习惯 Prisma,反而比原来更慢。工具没有好坏,匹配场景才重要。t3code 的价值在“全栈一体”的地方才能放大。

2. 环境准备与项目初始化:从零跑起一个 t3code 工程

2.1 初始化前需要准备的本地环境

在跑 create-t3-app 之前,先把本地环境确认一遍。我踩过最蠢的坑就是 Node 版本太老,脚手架装到一半报语法错误,排查半天才发现是 nvm 切错了版本。

最低要求如下:

  • Node.js 18 及以上,推荐 20 LTS
  • 包管理器用 npm 或 pnpm,我习惯用 pnpm,安装速度快,磁盘占用也小
  • Git,用来做初始化和版本管理
  • 数据库:开发阶段用 SQLite 最省事,文件即数据库;如果目标环境是 PostgreSQL,也可以提前连远程库

检查命令很简单:

node -v pnpm -v git --version

如果 Node 版本不够,用 nvm 切换一下就好。不要在这种基础环境上偷懒,后面所有问题排查都会因为你有一个“确定干净的起点”而简单很多。

2.2 使用 create-t3-app 生成工程

create-t3-app 是 T3 Stack 的官方脚手架,运行一条命令就能把项目骨架拉起来。

npm create t3-app@latest t3code-app

如果你用 pnpm:

pnpm create t3-app@latest t3code-app

执行后会有几个交互选择项,我逐个说下该怎么选:

  • TypeScript:必选,这是整套方案的地基
  • ESLint / Prettier:都选上,团队协作时能省掉大量格式口水仗
  • App Router 还是 Pages Router:新项目直接 App Router,这是 Next.js 当前主推的方向
  • tRPC、Prisma、NextAuth:按需选择。我建议第一次做就全选,t3code 的完整形态就靠它们撑起来
  • Tailwind:选上,哪怕你不喜欢原子类,先跑通再谈取舍

生成完进入目录,安装依赖:

cd t3code-app pnpm install

这里有个小提醒:create-t3-app 生成的项目会在启动时做环境变量校验,所以直接pnpm dev大概率会报错。你得先复制.env.example为.env,把数据库连接等关键配置填好。具体怎么填,看下一节。

2.3 目录结构与关键文件解读

生成后的目录结构大致如下,不同版本可能略有出入,但核心模块的位置是稳定的:

t3code-app/ ├─ app/ │ ├─ api/trpc/[trpc]/route.ts │ └─ ...页面组件 ├─ prisma/ │ ├─ schema.prisma │ └─ seed.ts ├─ server/ │ ├─ api/ │ │ ├─ routers/ │ │ ├─ root.ts │ │ └─ trpc.ts │ └─ db.ts ├─ trpc/ │ ├─ client.ts │ ├─ react.tsx │ └─ server.ts ├─ env.mjs └─ package.json

我挑几个重点文件解释一下:

server/api/trpc.ts是 tRPC 实例的创建处,里面会initTRPC,还会定义公共的 publicProcedure 和带鉴权的 protectedProcedure。所有中间件都在这里挂载。

server/api/root.ts是总路由聚合器。你写的每个业务子路由,比如 postRouter、userRouter,都在这里汇入 appRouter。前端类型AppRouter也是从它推导出来的。

app/api/trpc/[trpc]/route.ts是 Next.js 的 Route Handler,负责把 HTTP 请求转交给 tRPC 处理。如果你发现 tRPC 请求 404,八成是这个文件的路径或导出出了问题。

trpc/server.ts和trpc/client.ts分别对应服务端调用和客户端调用的入口。服务端调用可以让你在 Server Component 里直接取数据,不走额外的 HTTP 请求,这个后面细说。

prisma/schema.prisma是你的数据模型文件,改表结构就是改它。

2.4 环境变量配置细节

create-t3-app 默认会带一个env.mjs,它用@t3-oss/env-nextjs做启动时校验。也就是说,环境变量不对,服务根本起不来。这是好事,能在最开始就暴露问题,而不是等运行时才炸。

.env里至少要配这几项:

DATABASE_URL="file:./dev.db" NEXTAUTH_SECRET="随便生成一段长随机字符串" NEXTAUTH_URL="http://localhost:3000"

注意,NextAuth 的版本不同,变量名可能不同。新版本可能用AUTH_SECRET和AUTH_URL,具体以生成的.env.example为准。我的经验是:永远先看模板默认给了什么,再往上填,不要凭记忆乱写。

另外,强烈建议把.env加入.gitignore,只提交.env.example。这样别人 clone 项目后,复制一份示例文件就能快速启动,同时不会把你的数据库连接串和密钥泄露出去。团队协作时,这个习惯能避免不少尴尬。

3. 核心实操:用 tRPC 把 API 层做薄做准

3.1 tRPC 的基本概念:Router、Procedure、Middleware

tRPC 的逻辑其实很好理解。我通常用餐厅来类比:

  • Router 是菜单,告诉别人这家店有什么菜
  • Procedure 是单个菜品,可以理解为接口端点,分 query 和 mutation 两种
  • Middleware 是餐厅门口的安检,比如检查你是否有会员卡

query负责查数据,mutation负责改数据。这个区分和 React Query 的语义天然对应,前端调用时心智负担非常低。

先看 tRPC 实例的初始化:

// server/api/trpc.ts import { initTRPC, TRPCError } from "@trpc/server"; import type { Context } from "./context"; const t = initTRPC.context<Context>().create(); export const router = t.router; export const publicProcedure = t.procedure; const isAuthed = t.middleware(({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ ctx: { user: ctx.session.user } }); }); export const protectedProcedure = t.procedure.use(isAuthed);

这里最关键的是中间件。普通公开接口用publicProcedure,需要登录的接口用protectedProcedure,调用时它会先检查 session,没有登录就直接抛错,省得在每个业务代码里重复写鉴权判断。

3.2 定义第一个业务 Router:一个帖子模块

有了基础实例,写业务接口就很快了。我在 t3code 里先写了最经典的 post 模块,包含列表和创建两个接口。

// server/api/routers/post.ts import { z } from "zod"; import { router, publicProcedure, protectedProcedure } from "../trpc"; export const postRouter = router({ listRecent: publicProcedure.query(async ({ ctx }) => { return ctx.db.post.findMany({ take: 10, orderBy: { createdAt: "desc" }, }); }), create: protectedProcedure .input( z.object({ title: z.string().min(1).max(80), content: z.string().min(1), }) ) .mutation(async ({ ctx, input }) => { return ctx.db.post.create({ data: { title: input.title, content: input.content, authorId: ctx.user.id, }, }); }), });

我在项目里习惯把 Zod schema 直接写在路由文件里,因为这里的输入校验只属于这个接口,没必要拆到别处。Zod 的好处是前后端共用同一份校验规则,前端在调用create时,如果少传一个字段或者类型不对,编辑器立刻标红,根本不需要等后端返回 400。

然后把这组路由挂到总路由上:

// server/api/root.ts import { postRouter } from "./routers/post"; import { router } from "./trpc"; export const appRouter = router({ post: postRouter, }); export type AppRouter = typeof appRouter;

前端用到的所有类型,都是从AppRouter自动推导出来的。

3.3 前端调用:useQuery 与 useMutation 的爽点

create-t3-app 会生成一个utils/api.ts,它做的事情就是创建一个带类型的 tRPC React 绑定:

// utils/api.ts import { createTRPCReact } from "@trpc/react-query"; import type { AppRouter } from "~/server/api/root"; export const api = createTRPCReact<AppRouter>();

然后页面里就可以直接这样写:

function PostList() { const { data: posts, isLoading } = api.post.listRecent.useQuery(); const createPost = api.post.create.useMutation({ onSuccess: () => { // 这里触发列表重新拉取 }, }); if (isLoading) return <div>加载中...</div>; return ( <ul> {posts?.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }

我第一次用的时候,最大的震撼是调用createPost.mutate时,编辑器会自动弹出参数提示:title 是 string,content 是 string,多传一个字段直接编译报错。这在以前调 REST 接口时是根本不可能的,少参数、错类型只能等运行时再看。

还有一个小技巧:mutation 的onSuccess里,除了 refetch,还可以用api.post.listRecent.invalidate()让 React Query 失效并自动重新拉取。这个比手动 refetch 更优雅,也是官方推荐的做法。

3.4 权限控制:用中间件统一处理登录态

权限这块,我在第 3.1 节已经写了isAuthed中间件。它的作用是把 session 里的用户信息注入到ctx.user,之后的业务代码可以直接用。

中间件的好处特别明显:假设你有 6 个接口需要登录,传统写法每个接口都要写一遍“检查 token - 解析用户 - 处理异常”,而 t3code 里只需要在定义时换一个protectedProcedure。以后如果想调整鉴权逻辑,比如加个黑名单判断,只改中间件一处就能全局生效。

我在 t3code 里还加了一个小需求:只有作者本人能改自己的文章。做法是在接口里先查一遍post.authorId和ctx.user.id是否匹配:

update: protectedProcedure .input(z.object({ id: z.string(), title: z.string().min(1) })) .mutation(async ({ ctx, input }) => { const post = await ctx.db.post.findUnique({ where: { id: input.id } }); if (!post) throw new TRPCError({ code: "NOT_FOUND" }); if (post.authorId !== ctx.user.id) { throw new TRPCError({ code: "FORBIDDEN" }); } return ctx.db.post.update({ where: { id: input.id }, data: { title: input.title }, }); }),

这种业务级权限放到接口内部判断,比放在前端隐藏按钮可靠得多。前端隐藏只是一种体验优化,真正的安全边界永远要在服务端。

3.5 在 Server Component 里直接调用 tRPC

App Router 模式下,Next.js 支持在服务端组件里直接取数。tRPC 提供了一个 server caller,可以绕过 HTTP 请求直接调用 router。

// app/page.tsx import { api } from "~/trpc/server"; export default async function Home() { const posts = await api.post.listRecent(); return <PostList initData={posts} />; }

这样做的价值在于:首屏不产生额外的网络往返。传统方案里,浏览器先加载 HTML,再发 Ajax 请求拿数据,中间少说几百毫秒;而 server caller 在服务端直接查数据库,把结果渲染进页面,用户打开页面就是完整内容。

这招适合对首屏速度敏感的场景。但也别滥用,服务端组件里直接查库意味着每次页面刷新都触发查询,如果页面是强交互型,反而用客户端 query 更灵活。我一般是:首屏数据用 server caller,交互后的数据用前端 useQuery。

4. 数据层:Prisma 建模、迁移与种子数据

4.1 Schema 建模:从业务出发定字段

t3code 的数据层用的是 Prisma。它的 schema 文件写起来很像在定义对象,字段、关系、索引都在一个文件里管理。我先定义一个 User 和 Post 的双表结构:

model User { id String @id @default(cuid()) name String? email String? @unique posts Post[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Post { id String @id @default(cuid()) title String @db.VarChar(80) content String @db.Text published Boolean @default(false) author User @relation(fields: [authorId], references: [id], onDelete: Cascade) authorId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([authorId, createdAt]) }

几点注意:

cuid()作为默认 ID 生成方式,适合分布式环境,不会像自增数字那样暴露业务量。如果你能确定项目规模不大,用@default(autoincrement())也可以,但我不太喜欢把业务 ID 交给数据库自增,所以一直用 cuid。

@db.VarChar(80)和@db.Text是在告诉数据库用具体的列类型。标题用变长字符串,正文用长文本,这种显式声明会让数据库层面的存储更合理。

复合索引@@index([authorId, createdAt])很有用。常见的文章列表查询就是“某个用户的所有文章按时间倒序”,这个索引能覆盖该查询条件,避免全表扫描。项目刚起步时数据量不大看不出来差距,等数据到了十万级,索引有没有效果是天壤之别。

4.2 迁移与 Seed:让数据库跟上代码

写完 schema,要执行迁移。开发环境用:

npx prisma migrate dev --name init

这个命令会做三件事:对比 schema 变化、生成迁移 SQL、执行到本地数据库。如果本地用的是 SQLite,它还会自动建库文件,非常方便。

seed 我建议一定要做。没有测试数据,开发页面时总是空荡荡的,调试体验极差。在package.json里加上 seed 配置:

"prisma": { "seed": "tsx prisma/seed.ts" }

然后写一个prisma/seed.ts:

import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { const user = await prisma.user.upsert({ where: { email: "demo@example.com" }, update: {}, create: { email: "demo@example.com", name: "Demo User" }, }); await prisma.post.createMany({ data: Array.from({ length: 10 }).map((_, i) => ({ title: `第 ${i + 1} 篇文章`, content: "这里是种子数据内容", authorId: user.id, })), }); } main() .then(() => prisma.$disconnect()) .catch((e) => { console.error(e); process.exit(1); });

执行npx prisma db seed,数据库里就有了 1 个用户和 10 篇文章。upsert是个好函数,能避免重复执行 seed 时产生重复数据。

重点提醒:生产环境不要用migrate dev。它会要求交互确认,还可能重置数据。生产环境应该用prisma migrate deploy,它只执行未应用的迁移文件,不会重置任何东西。这是新手最常犯的错误之一。

4.3 在 tRPC 中安全地查库

Prisma 和 tRPC 搭配起来,最舒服的是不需要写 DTO。tRPC 的 query 直接返回 Prisma 对象,前端拿到的是什么形状,类型已经定死了。但这不代表可以偷懒,我一直遵循几条原则:

第一,只查需要的字段。用select或include控制返回内容,而不是把整张表的数据都丢给前端。

list: publicProcedure.query(async ({ ctx }) => { return ctx.db.post.findMany({ select: { id: true, title: true, published: true, }, }); }),

第二,找不到数据时明确抛错。

detail: publicProcedure .input(z.object({ id: z.string() })) .query(async ({ ctx, input }) => { const post = await ctx.db.post.findUnique({ where: { id: input.id } }); if (!post) throw new TRPCError({ code: "NOT_FOUND" }); return post; }),

用TRPCError而不是直接返回null,好处是前端能拿到明确的错误码,后续做错误统一处理会更方便。

第三,关系字段名要起得让人看得懂。authorId和author这种命名,比userId和user更容易在业务代码里分辨。

5. 常见问题与排查技巧实录

5.1 改了表结构,前端类型没有更新

这个是我身边朋友问得最多的一个问题。明明是schema.prisma里加了字段,也跑了迁移,为什么前端代码还是不认识这个新字段?

原因基本只有一个:Prisma Client 没有重新生成。prisma migrate dev本身会触发 generate,但如果你只改了 schema 忘了跑迁移,或者在不同终端操作,就有可能出现这种问题。

解决办法很简单:

npx prisma generate

如果你用了 path alias,也可以在 package.json 里配一个脚本,比如"db:generate": "prisma generate",平时直接pnpm db:generate。改完 schema 后先 format 再 generate,是我的习惯动作。

5.2 启动即崩:环境变量校验失败

create-t3-app 的env.mjs会在启动时校验环境变量,一旦某个变量缺失或格式不对,进程会直接退出,控制台提示也很明显。

我的排查顺序是:

  • 打开.env,对照.env.example逐项检查
  • 看DATABASE_URL是否指向有效数据库。开发环境最快的是 SQLite:file:./dev.db
  • 如果是 PostgreSQL,确认连接串里没有特殊字符被 shell 转义
  • 确认密钥长度足够,尤其是NEXTAUTH_SECRET或AUTH_SECRET,随便运行一段随机字符串生成命令就行

这个机制看起来冷酷,其实很温柔。把配置错误暴露在启动前,比用户访问时才发现页面崩了要好一万倍。

5.3 tRPC 接口 404 或冷启动报错

接口 404 的原因,多半是 Route Handler 路径不对。确认你的文件确实在app/api/trpc/[trpc]/route.ts,并且导出了handler:

import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; import { appRouter } from "@/server/api/root"; import { createTRPCContext } from "@/server/api/trpc"; export const runtime = "nodejs"; const handler = (req: Request) => fetchRequestHandler({ endpoint: "/api/trpc", req, router: appRouter, createContext: createTRPCContext, }); export { handler as GET, handler as POST };

注意export const runtime = "nodejs"这行不能省。Next.js 默认可能把路由优化成 Edge Runtime,而 Prisma 在 Edge Runtime 下跑会有兼容性问题。加上这行,强制用 Node.js 运行时,是我排查冷启动报错时最常发现的解法。

5.4 部署到 Vercel 时的坑

t3code 这类项目部署到 Vercel 很顺,但有几个暗坑。

数据库连接数是最容易出问题的点。Vercel 的 Serverless 函数会按请求启动,如果每个实例都新建一个 Prisma 连接,高并发时数据库会被打爆。解决思路有两个:用外置连接池,比如 PgBouncer;或者给 Prisma 接上加速器。如果你只是中小流量,尽量在数据库配置里调大连接数上限,同时在全局复用 Prisma 实例。

另一个坑是 SQLite 不能用于线上。SQLite 是本地文件型数据库,部署到 Serverless 环境后根本没有持久化磁盘,所以生产环境必须切 PostgreSQL。我的做法是开发环境用 SQLite 保速度,生产环境用 PostgreSQL 保稳定,两套配置靠.env分离。

Vercel 后台别忘了填环境变量。我见过很多本地跑得好好的项目,推到 Vercel 上立刻崩,都是因为这边只提交了代码,没把.env里的变量搬到 Vercel 的 Environment Variables 面板。

5.5 常见问题速查表

问题可能原因排查/解决
tRPC 请求 404Route Handler 路径错误或 runtime 不对检查app/api/trpc/[trpc]/route.ts,加runtime = "nodejs"
数据库表不存在没执行迁移npx prisma migrate dev/migrate deploy
登录接口报 UNAUTHORIZEDsession 获取失败检查 NextAuth 配置、secret、cookie 域名
前端类型对不上Prisma Client 版本过期或代码未同步pnpm db:generate+ 全项目 typecheck
页面刷新后登录态丢失cookie 作用域设置不当确认NEXTAUTH_URL和 domain 配置
部署构建失败Vercel 环境变量缺失检查后台所有 server 端变量

6. 我的实操体会与后续扩展思路

6.1 用了一段 t3code 之后的真实感受

t3code 这套组合,我用下来最大的感受是“舒服”,不是“酷炫”。最典型的例子是:我改后端接口参数类型时,前端代码里所有调用点会同时爆红,根本不存在“我改了后端忘了前端”的情况。对于一个人维护全栈的人来说,这种编译期防线比任何测试都能兜底。

但它也有代价。tRPC 对跨团队协作其实不太友好,因为接口文档需要额外沉淀,否则后来者只能通过读代码理解业务逻辑。另外,调试 tRPC 调用时,错误信息没有 REST 那么直观,有时候需要顺着服务端堆栈定位问题。我的经验是:项目里要有一个统一的错误处理约定,并且把 Zod 校验错误通过TRPCError包装好,前端才能拿到可读的提示。

还有一个容易忽略的小知识点:不要因为 tRPC 方便就在前端把所有数据库查询逻辑都直连暴露。任何涉及多租户数据隔离的查询,都必须通过受控的 procedure 暴露,不能为了省事跳过鉴权中间件。安全和便利之间,前者永远优先。

6.2 还可以往哪些方向扩展

t3code 现在只是一套基础骨架,但它的扩展空间很大。我列几个我实际试过或正在计划的方向,供你参考。

文件上传可以用 UploadThing,它的类型安全和 T3 组合得很自然。如果觉得 NextAuth 太重,可以换成 Clerk,托管式登录能省不少事,只是要注意别被锁死。数据库查询如果越来越复杂,可以引入 t3-resolver 这类查询优化器,通过 Stored Procedure 把复杂查询放到数据库里执行。部署层面,如果不想用 Vercel,Docker + Nginx 也是完全可行的方案,只是需要自己处理环境变量和数据库连接。

最后再分享一个小技巧:如果你开始往项目里加 eslint 规则,建议把@tanstack/query的依赖状态检查也开起来,mutation 完成后忘记 invalidate 是最常见的数据不同步来源。我自己在这上面栽了好几次,后来养成了“每次 mutation 成功后立刻想一遍哪些 query 需要失效”的习惯,页面就很少出现旧数据了。

t3code 这套东西,本质上不是复杂的技术,而是一套让你把精力集中在业务上的工程组合。希望这篇分享能帮你少走点弯路,把时间省下来解决真正的问题。

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

pstack与Claude协同实现系统级AI故障诊断

1. 项目概述&#xff1a;pstack-claude 是什么&#xff0c;它解决的是哪类开发者的实际痛点&#xff1f; “pstack-claude”这个名称乍看像一个工具组合词&#xff0c;但拆解后立刻能抓住核心脉络&#xff1a; pstack 是 Linux 系统中用于打印进程调用栈&#xff08;stack tr…

作者头像 李华
网站建设 2026/10/26 23:32:49

AI几何教学工具:大角几何的核心功能与应用

1. 项目概述&#xff1a;当几何教学遇上AI工具去年给高三艺考生补课的经历让我彻底改变了教学工具包。面对每周只有2小时课时却要覆盖立体几何全部考点的情况&#xff0c;传统尺规作图根本满足不了效率需求。直到发现大角几何这款专为数学教学设计的智能工具&#xff0c;才真正…

作者头像 李华
网站建设 2026/10/26 23:32:07

车辆ACC控制技术:原理、算法与工程实践

1. 车辆ACC控制技术概述 自适应巡航控制&#xff08;Adaptive Cruise Control&#xff0c;简称ACC&#xff09;作为现代汽车智能驾驶系统的核心功能之一&#xff0c;已经从高端车型的专属配置逐步下放到主流家用车市场。这项技术本质上是通过车载传感器&#xff08;毫米波雷达、…

作者头像 李华
网站建设 2026/10/26 23:29:57

t3code:从Prisma Schema自动生成T3全栈CRUD代码

把第三个业务模块的手写CRUD代码推倒重写的时候&#xff0c;我终于承认自己的问题不是写得慢&#xff0c;是写得烦。明明就是 list、get、create、update、delete 这几板斧&#xff0c;换张表名就要从头复制粘贴一遍&#xff0c;还得小心翼翼改掉所有驼峰命名、类型导入和 tRPC…

作者头像 李华
网站建设 2026/10/26 23:28:50

香橙派免密SSH连接配置指南

1. 项目概述香橙派作为一款高性价比的单板计算机&#xff0c;在物联网和嵌入式开发领域广受欢迎。但在日常开发中&#xff0c;频繁输入SSH密码连接设备既低效又存在安全隐患。本文将详细介绍如何在局域网环境下实现香橙派的免密SSH连接方案&#xff0c;这个技巧对于需要频繁操作…

作者头像 李华
网站建设 2026/10/26 23:28:45

AI大模型在传统命理分析中的表现与评测

1. 项目背景与测试动机 最近两年&#xff0c;各类AI大模型在专业领域的表现成为业界关注焦点。作为一名长期研究传统文化与现代科技结合的从业者&#xff0c;我注意到市面上出现了大量号称能够进行八字排盘、紫微斗数分析的AI工具。这些工具往往标榜自己"准确率高达90%&qu…

作者头像 李华