mcpField
T
trpc

trpc

github.com/mindrally开发287872 次安装3 周前更新
代码仓库

关于 trpc

这是一项使用 tRPC v11、TypeScript 和 Next.js 构建端到端类型安全 API 的实践技能,涵盖服务端与客户端配置、验证、授权和 React Query 集成。

主要功能

  1. 01端到端类型推断
  2. 02Zod 输入验证
  3. 03按功能组织路由器
  4. 04中间件与授权模式
  5. 05React Query 数据工具

适用场景

  1. 01按领域组织 tRPC API
  2. 02验证过程输入
  3. 03添加受保护过程与中间件
  4. 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