关于 trpc
这是一项使用 tRPC v11、TypeScript 和 Next.js 构建端到端类型安全 API 的实践技能,涵盖服务端与客户端配置、验证、授权和 React Query 集成。
主要功能
- 01端到端类型推断
- 02Zod 输入验证
- 03按功能组织路由器
- 04中间件与授权模式
- 05React Query 数据工具
适用场景
- 01按领域组织 tRPC API
- 02验证过程输入
- 03添加受保护过程与中间件
- 04在 React 中获取和变更数据
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mindrally/ skills --skill trpc安装后在 Claude Code 中输入/trpc确认已加载。
详细介绍
tRPC 最佳实践
你精通 tRPC v11、TypeScript 和 Next.js 开发。tRPC 支持端到端的类型安全 API,让你无需架构、代码生成或担心运行时错误,即可构建和使用 API。
要求
- TypeScript >= 5.7.2
- 启用严格 TypeScript 模式
项目结构
src/
pages/
_app.tsx # createTRPCNext setup
api/
trpc/
[trpc].ts # tRPC HTTP handler
server/
routers/
_app.ts # Main router
[feature].ts # Feature-specific routers
context.ts # App context
trpc.ts # Procedure helpers
utils/
trpc.ts # Typesafe React hooks
服务端设置
初始化 tRPC 后端
在每个应用中只初始化一次 tRPC 后端。导出可复用的路由器和过程辅助函数:
// server/trpc.ts
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
const t = initTRPC.context<Context>().create({
transformer: superjson,
});
export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);
export const adminProcedure = t.procedure.use(isAdmin);
创建功能路由器
按功能/领域组织路由器。使用 Zod 验证输入:
// server/routers/user.ts
import { z } from 'zod';
import { router, publicProcedure, protectedProcedure } from '../trpc';
export const userRouter = router({
getById: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input, ctx }) => {
return ctx.db.user.findUnique({ where: { id: input.id } });
}),
update: protectedProcedure
.input(z.object({
name: z.string().min(1),
email: z.string().email(),
}))
.mutation(async ({ input, ctx }) => {
return ctx.db.user.update({
where: { id: ctx.user.id },
data: input,
});
}),
});
主应用路由器
// server/routers/_app.ts
import { router } from '../trpc';
import { userRouter } from './user';
import { postRouter } from './post';
export const appRouter = router({
user: userRouter,
post: postRouter,
});
export type AppRouter = typeof appRouter;
客户端设置
配置 tRPC 客户端
// utils/trpc.ts
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import superjson from 'superjson';
import type { AppRouter } from '../server/routers/_app';
function getBaseUrl() {
if (typeof window !== 'undefined') return '';
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
return `http://localhost:${process.env.PORT ?? 3000}`;
}
export const trpc = createTRPCNext<AppRouter>({
config() {
return {
transformer: superjson,
links: [
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
maxURLLength: 2083,
}),
],
};
},
ssr: false,
});
关键最佳实践
1. 输入验证
始终使用 Zod 对所有过程输入进行类型安全检查和运行时验证:
.input(z.object({
email: z.string().email(),
age: z.number().min(0).max(120),
}))
2. 路由器组织
按功能/领域组织路由器,而不是使用一个庞大的路由器。每项功能都应有自己的路由器文件。
3. 中间件实现
实现用于身份验证、日志记录及横切关注点的中间件:
const isAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({ ctx: { user: ctx.user } });
});
4. 错误处理
使用 TRPCError 返回一致且信息明确的错误响应:
import { TRPCError } from '@trpc/server';
throw new TRPCError({
code: 'NOT_FOUND',
message: 'User not found',
});
5. 数据转换器
应用 SuperJSON,自动序列化日期、Map、Set 和其他 JavaScript 类型:
import superjson from 'superjson';
const t = initTRPC.create({
transformer: superjson,
});
6. React Query 集成
利用 tRPC 内置的 React Query 工具进行数据获取、变更和缓存:
// Queries
const { data, isLoading } = trpc.user.getById.useQuery({ id: '123' });
// Mutations
const mutation = trpc.user.update.useMutation({
onSuccess: () => {
utils.user.getById.invalidate();
},
});
// Prefetching
await utils.user.getById.prefetch({ id: '123' });
7. 上下文创建
通过上下文在各过程中共享资源(数据库连接、用户会话):
// server/context.ts
export async function createContext({ req, res }: CreateNextContextOptions) {
const user = await getUser(req);
return {
db: prisma,
user,
req,
res,
};
}
export type Context = Awaited<ReturnType<typeof createContext>>;
8. 类型导出
仅向客户端代码导出类型签名,绝不导出路由器实现:
// Only export the type
export type AppRouter = typeof appRouter;
// Never export the actual router to client code
9. 授权级别
为不同的授权级别创建不同的过程类型:
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);
export const adminProcedure = t.procedure.use(isAuthed).use(isAdmin);
10. 性能优化
- 使用 httpBatchLink 启用请求批处理
- 配置 maxURLLength,避免 URL 长度问题
- 为静态/可预测的数据实现预取
- 使用 React Query 的缓存策略
应避免的反模式
- 不要使用
any类型——充分利用 tRPC 的类型推断 - 不要跳过输入验证
- 不要向客户端暴露内部错误
- 不要混用服务端和客户端代码
- 不要创建过大的路由器——按功能拆分
评论
支持 Markdown