tRPCPatterns tRPCPatterns

tRPC是一种用于构建端到端类型安全API的技术,它允许在客户端和服务器之间无缝集成TypeScript,无需代码生成。

后端开发 0 次安装 0 次浏览 更新于 3/5/2026

name: tRPC Patterns description: 端到端类型安全的APIs与tRPC,实现客户端和服务器之间的无缝TypeScript集成,无需代码生成。

tRPC Patterns

概览

tRPC使得构建无需编写单独模式或生成代码的类型安全API成为可能。类型在客户端和服务器之间自动共享,非常适合TypeScript monorepos和全栈应用。

为什么这很重要

  • 零代码生成:自动推断类型,无需代码生成步骤
  • 端到端类型安全:在编译时捕获API错误
  • 出色的开发体验:IDE中API调用的自动补全
  • 小包:客户端开销最小

核心概念

1. 路由器定义

// server/trpc.ts
import { initTRPC, TRPCError } from '@trpc/server';
import { Context } from './context';

const t = initTRPC.context<Context>().create();

export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);

// 中间件
const isAuthed = t.middleware(({ ctx, next }) => {
  if (!ctx.user) {
    throw new TRPCError({ code: 'UNAUTHORIZED' });
  }
  return next({ ctx: { user: ctx.user } });
});

2. 程序(端点)

// server/routers/user.ts
import { z } from 'zod';
import { router, publicProcedure, protectedProcedure } from '../trpc';

export const userRouter = router({
  // 查询(GET)
  getById: publicProcedure
    .input(z.object({ id: z.string().uuid() }))
    .query(async ({ input, ctx }) => {
      return ctx.prisma.user.findUnique({ where: { id: input.id } });
    }),

  // 变更(POST/PUT/DELETE)
  updateProfile: protectedProcedure
    .input(z.object({
      name: z.string().min(1).max(100),
      bio: z.string().max(500).optional(),
    }))
    .mutation(async ({ input, ctx }) => {
      return ctx.prisma.user.update({
        where: { id: ctx.user.id },
        data: input,
      });
    }),

  // 订阅(WebSocket)
  onNewMessage: protectedProcedure
    .subscription(({ ctx }) => {
      return observable<Message>((emit) => {
        const onMessage = (msg: Message) => emit.next(msg);
        ctx.ee.on('newMessage', onMessage);
        return () => ctx.ee.off('newMessage', onMessage);
      });
    }),
});

3. 应用路由器

// server/routers/_app.ts
import { router } from '../trpc';
import { userRouter } from './user';
import { postRouter } from './post';
import { commentRouter } from './comment';

export const appRouter = router({
  user: userRouter,
  post: postRouter,
  comment: commentRouter,
});

export type AppRouter = typeof appRouter;

4. 客户端设置(React)

// utils/trpc.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../server/routers/_app';

export const trpc = createTRPCReact<AppRouter>();

// 提供器设置
function App() {
  const [queryClient] = useState(() => new QueryClient());
  const [trpcClient] = useState(() =>
    trpc.createClient({
      links: [
        httpBatchLink({
          url: '/api/trpc',
          headers: () => ({
            authorization: getAuthToken(),
          }),
        }),
      ],
    }),
  );

  return (
    <trpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        <MyApp />
      </QueryClientProvider>
    </trpc.Provider>
  );
}

5. 在组件中使用

// 完全类型安全和自动补全!
function UserProfile({ userId }: { userId: string }) {
  // 查询
  const { data: user, isLoading } = trpc.user.getById.useQuery({ id: userId });

  // 变更
  const updateProfile = trpc.user.updateProfile.useMutation({
    onSuccess: () => {
      utils.user.getById.invalidate({ id: userId });
    },
  });

  // 乐观更新
  const utils = trpc.useUtils();

  const handleUpdate = (name: string) => {
    updateProfile.mutate({ name });
  };

  if (isLoading) return <Spinner />;
  return <div>{user?.name}</div>;
}

快速开始

  1. 安装包:

    npm install @trpc/server @trpc/client @trpc/react-query @tanstack/react-query zod
    
  2. 创建tRPC实例:

    // server/trpc.ts
    import { initTRPC } from '@trpc/server';
    const t = initTRPC.create();
    export const router = t.router;
    export const publicProcedure = t.procedure;
    
  3. 定义你的路由器:

    export const appRouter = router({
      hello: publicProcedure
        .input(z.object({ name: z.string() }))
        .query(({ input }) => `Hello ${input.name}`),
    });
    
  4. 设置API处理器(Next.js):

    // pages/api/trpc/[trpc].ts
    import { createNextApiHandler } from '@trpc/server/adapters/next';
    export default createNextApiHandler({ router: appRouter });
    
  5. 在组件中使用:

    const { data } = trpc.hello.useQuery({ name: 'World' });
    

生产清单

  • [ ] 使用自定义错误格式化进行错误处理
  • [ ] 使用Zod模式进行输入验证
  • [ ] 速率限制中间件
  • [ ] 认证中间件
  • [ ] 请求日志记录
  • [ ] 响应缓存策略
  • [ ] 为性能配置批处理链接
  • [ ] 如果需要,设置WebSocket订阅
  • [ ] 为外部消费者生成OpenAPI(可选)

反模式

  1. 跳过输入验证:始终使用Zod模式进行输入验证
  2. 庞大的路由器:按领域拆分路由器,不要将所有内容放在一个文件中
  3. 程序中的业务逻辑:保持程序简洁,委托给服务
  4. 忽略错误处理:使用适当的TRPCError代码进行客户端处理

集成点

  • Next.js@trpc/server/adapters/next
  • Express@trpc/server/adapters/express
  • Fastify@trpc/server/adapters/fastify
  • Prisma:在上下文中直接集成
  • NextAuth:在上下文中会话
  • React Query@trpc/react-query

进一步阅读