[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FS0191BSP3T2M7NPX8WEMR":3,"skill-same:01M6FS0191BSP3T2M7NPX8WEMR":70},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":46,"repositoryHtmlUrl":47,"repositoryHomepageUrl":6,"ownerHtmlUrl":48,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":49,"install":50,"resources":63},"01M6FS0191BSP3T2M7NPX8WEMR","tRPC",null,"Expert guidance for tRPC (TypeScript Remote Procedure Call) including router setup, procedures, middleware, context, client configuration, and Next.js integration. Use this when building type-safe APIs, integrating tRPC with Next.js, or implementing client-server communication with full TypeScript inference.","kbarbel640-del\u002Fskills","kbarbel640-del","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F255315490?v=4","01M6CJPPQGCCESXMG3K7BBGQ5J","01M6CJPPX2PB2BCZERFVM2C2B3",1,"MIT","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fifoster01\u002Ftrpc-best-practices\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"trpc","expert","with","remote","middleware","client","type","full","next","call","safe","guidance","router","use","context","Next.js","server","integration","communication","typescript","2026-11-10 10:12:30",false,"---\nname: tRPC\ndescription: Expert guidance for tRPC (TypeScript Remote Procedure Call) including router setup, procedures, middleware, context, client configuration, and Next.js integration. Use this when building type-safe APIs, integrating tRPC with Next.js, or implementing client-server communication with full TypeScript inference.\n---\n\n# tRPC\n\nExpert assistance with tRPC - End-to-end typesafe APIs with TypeScript.\n\n## Overview\n\ntRPC enables building fully typesafe APIs without schemas or code generation:\n- Full TypeScript inference from server to client\n- No code generation needed\n- Excellent DX with autocomplete and type safety\n- Works great with Next.js, React Query, and more\n\n## Quick Start\n\n### Installation\n```bash\n# Core packages\nnpm install @trpc\u002Fserver@next @trpc\u002Fclient@next @trpc\u002Freact-query@next\n\n# Peer dependencies\nnpm install @tanstack\u002Freact-query@latest zod\n```\n\n### Basic Setup (Next.js App Router)\n\n**1. Create tRPC Router**\n```typescript\n\u002F\u002F server\u002Ftrpc.ts\nimport { initTRPC } from '@trpc\u002Fserver'\nimport { z } from 'zod'\n\nconst t = initTRPC.create()\n\nexport const router = t.router\nexport const publicProcedure = t.procedure\n```\n\n**2. Define API Router**\n```typescript\n\u002F\u002F server\u002Frouters\u002F_app.ts\nimport { router, publicProcedure } from '..\u002Ftrpc'\nimport { z } from 'zod'\n\nexport const appRouter = router({\n  hello: publicProcedure\n    .input(z.object({ name: z.string() }))\n    .query(({ input }) => {\n      return { greeting: `Hello ${input.name}!` }\n    }),\n\n  createUser: publicProcedure\n    .input(z.object({\n      name: z.string(),\n      email: z.string().email(),\n    }))\n    .mutation(async ({ input }) => {\n      const user = await db.user.create({ data: input })\n      return user\n    }),\n})\n\nexport type AppRouter = typeof appRouter\n```\n\n**3. Create API Route**\n```typescript\n\u002F\u002F app\u002Fapi\u002Ftrpc\u002F[trpc]\u002Froute.ts\nimport { fetchRequestHandler } from '@trpc\u002Fserver\u002Fadapters\u002Ffetch'\nimport { appRouter } from '@\u002Fserver\u002Frouters\u002F_app'\n\nconst handler = (req: Request) =>\n  fetchRequestHandler({\n    endpoint: '\u002Fapi\u002Ftrpc',\n    req,\n    router: appRouter,\n    createContext: () => ({}),\n  })\n\nexport { handler as GET, handler as POST }\n```\n\n**4. Setup Client Provider**\n```typescript\n\u002F\u002F app\u002Fproviders.tsx\n'use client'\n\nimport { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query'\nimport { httpBatchLink } from '@trpc\u002Fclient'\nimport { useState } from 'react'\nimport { trpc } from '@\u002Flib\u002Ftrpc'\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n  const [queryClient] = useState(() => new QueryClient())\n  const [trpcClient] = useState(() =>\n    trpc.createClient({\n      links: [\n        httpBatchLink({\n          url: 'http:\u002F\u002Flocalhost:3000\u002Fapi\u002Ftrpc',\n        }),\n      ],\n    })\n  )\n\n  return (\n    \u003Ctrpc.Provider client={trpcClient} queryClient={queryClient}>\n      \u003CQueryClientProvider client={queryClient}>\n        {children}\n      \u003C\u002FQueryClientProvider>\n    \u003C\u002Ftrpc.Provider>\n  )\n}\n```\n\n**5. Create tRPC Client**\n```typescript\n\u002F\u002F lib\u002Ftrpc.ts\nimport { createTRPCReact } from '@trpc\u002Freact-query'\nimport type { AppRouter } from '@\u002Fserver\u002Frouters\u002F_app'\n\nexport const trpc = createTRPCReact\u003CAppRouter>()\n```\n\n**6. Use in Components**\n```typescript\n'use client'\n\nimport { trpc } from '@\u002Flib\u002Ftrpc'\n\nexport default function Home() {\n  const hello = trpc.hello.useQuery({ name: 'World' })\n  const createUser = trpc.createUser.useMutation()\n\n  return (\n    \u003Cdiv>\n      \u003Cp>{hello.data?.greeting}\u003C\u002Fp>\n      \u003Cbutton\n        onClick={() => createUser.mutate({\n          name: 'John',\n          email: 'john@example.com'\n        })}\n      >\n        Create User\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n## Router Definition\n\n### Basic Router\n```typescript\nimport { router, publicProcedure } from '.\u002Ftrpc'\nimport { z } from 'zod'\n\nexport const userRouter = router({\n  \u002F\u002F Query - for fetching data\n  getById: publicProcedure\n    .input(z.string())\n    .query(async ({ input }) => {\n      return await db.user.findUnique({ where: { id: input } })\n    }),\n\n  \u002F\u002F Mutation - for creating\u002Fupdating\u002Fdeleting\n  create: publicProcedure\n    .input(z.object({\n      name: z.string(),\n      email: z.string().email(),\n    }))\n    .mutation(async ({ input }) => {\n      return await db.user.create({ data: input })\n    }),\n\n  \u002F\u002F Subscription - for real-time updates\n  onUpdate: publicProcedure\n    .subscription(() => {\n      return observable\u003CUser>((emit) => {\n        \u002F\u002F Implementation\n      })\n    }),\n})\n```\n\n### Nested Routers\n```typescript\nimport { router } from '.\u002Ftrpc'\nimport { userRouter } from '.\u002Frouters\u002Fuser'\nimport { postRouter } from '.\u002Frouters\u002Fpost'\nimport { commentRouter } from '.\u002Frouters\u002Fcomment'\n\nexport const appRouter = router({\n  user: userRouter,\n  post: postRouter,\n  comment: commentRouter,\n})\n\n\u002F\u002F Usage on client:\n\u002F\u002F trpc.user.getById.useQuery('123')\n\u002F\u002F trpc.post.list.useQuery()\n\u002F\u002F trpc.comment.create.useMutation()\n```\n\n### Merging Routers\n```typescript\nimport { router, publicProcedure } from '.\u002Ftrpc'\n\nconst userRouter = router({\n  list: publicProcedure.query(() => {\u002F* ... *\u002F}),\n  getById: publicProcedure.input(z.string()).query(() => {\u002F* ... *\u002F}),\n})\n\nconst postRouter = router({\n  list: publicProcedure.query(() => {\u002F* ... *\u002F}),\n  create: publicProcedure.input(z.object({})).mutation(() => {\u002F* ... *\u002F}),\n})\n\n\u002F\u002F Merge into app router\nexport const appRouter = router({\n  user: userRouter,\n  post: postRouter,\n})\n```\n\n## Input Validation with Zod\n\n### Basic Validation\n```typescript\nimport { z } from 'zod'\n\nexport const userRouter = router({\n  create: publicProcedure\n    .input(z.object({\n      name: z.string().min(2).max(50),\n      email: z.string().email(),\n      age: z.number().int().positive().optional(),\n      role: z.enum(['user', 'admin']),\n    }))\n    .mutation(async ({ input }) => {\n      \u002F\u002F input is fully typed!\n      return await db.user.create({ data: input })\n    }),\n})\n```\n\n### Complex Validation\n```typescript\nconst createPostInput = z.object({\n  title: z.string().min(5).max(100),\n  content: z.string().min(10),\n  published: z.boolean().default(false),\n  tags: z.array(z.string()).min(1).max(5),\n  metadata: z.object({\n    views: z.number().default(0),\n    likes: z.number().default(0),\n  }).optional(),\n})\n\nexport const postRouter = router({\n  create: publicProcedure\n    .input(createPostInput)\n    .mutation(async ({ input }) => {\n      return await db.post.create({ data: input })\n    }),\n})\n```\n\n### Reusable Schemas\n```typescript\n\u002F\u002F schemas\u002Fuser.ts\nexport const userSchema = z.object({\n  id: z.string(),\n  name: z.string(),\n  email: z.string().email(),\n})\n\nexport const createUserSchema = userSchema.omit({ id: true })\nexport const updateUserSchema = userSchema.partial()\n\n\u002F\u002F Use in router\nexport const userRouter = router({\n  create: publicProcedure\n    .input(createUserSchema)\n    .mutation(({ input }) => {\u002F* ... *\u002F}),\n\n  update: publicProcedure\n    .input(z.object({\n      id: z.string(),\n      data: updateUserSchema,\n    }))\n    .mutation(({ input }) => {\u002F* ... *\u002F}),\n})\n```\n\n## Context\n\n### Creating Context\n```typescript\n\u002F\u002F server\u002Fcontext.ts\nimport { inferAsyncReturnType } from '@trpc\u002Fserver'\nimport { FetchCreateContextFnOptions } from '@trpc\u002Fserver\u002Fadapters\u002Ffetch'\n\nexport async function createContext(opts: FetchCreateContextFnOptions) {\n  \u002F\u002F Get session from cookies\u002Fheaders\n  const session = await getSession(opts.req)\n\n  return {\n    session,\n    db,\n  }\n}\n\nexport type Context = inferAsyncReturnType\u003Ctypeof createContext>\n```\n\n### Using Context in tRPC\n```typescript\n\u002F\u002F server\u002Ftrpc.ts\nimport { initTRPC } from '@trpc\u002Fserver'\nimport { Context } from '.\u002Fcontext'\n\nconst t = initTRPC.context\u003CContext>().create()\n\nexport const router = t.router\nexport const publicProcedure = t.procedure\n```\n\n### Accessing Context in Procedures\n```typescript\nexport const userRouter = router({\n  me: publicProcedure.query(({ ctx }) => {\n    \u002F\u002F ctx.session, ctx.db are available\n    if (!ctx.session) {\n      throw new TRPCError({ code: 'UNAUTHORIZED' })\n    }\n\n    return ctx.db.user.findUnique({\n      where: { id: ctx.session.userId }\n    })\n  }),\n})\n```\n\n## Middleware\n\n### Creating Middleware\n```typescript\n\u002F\u002F server\u002Ftrpc.ts\nimport { initTRPC, TRPCError } from '@trpc\u002Fserver'\n\nconst t = initTRPC.context\u003CContext>().create()\n\n\u002F\u002F Logging middleware\nconst loggerMiddleware = t.middleware(async ({ path, type, next }) => {\n  const start = Date.now()\n  const result = await next()\n  const duration = Date.now() - start\n\n  console.log(`${type} ${path} took ${duration}ms`)\n\n  return result\n})\n\n\u002F\u002F Auth middleware\nconst isAuthed = t.middleware(({ ctx, next }) => {\n  if (!ctx.session) {\n    throw new TRPCError({ code: 'UNAUTHORIZED' })\n  }\n\n  return next({\n    ctx: {\n      \u002F\u002F Infers session is non-nullable\n      session: ctx.session,\n    },\n  })\n})\n\n\u002F\u002F Create procedures with middleware\nexport const publicProcedure = t.procedure.use(loggerMiddleware)\nexport const protectedProcedure = t.procedure.use(loggerMiddleware).use(isAuthed)\n```\n\n### Using Protected Procedures\n```typescript\nexport const postRouter = router({\n  \u002F\u002F Public - anyone can access\n  list: publicProcedure.query(() => {\n    return db.post.findMany({ where: { published: true } })\n  }),\n\n  \u002F\u002F Protected - requires authentication\n  create: protectedProcedure\n    .input(z.object({ title: z.string() }))\n    .mutation(({ ctx, input }) => {\n      \u002F\u002F ctx.session is guaranteed to exist\n      return db.post.create({\n        data: {\n          ...input,\n          authorId: ctx.session.userId,\n        },\n      })\n    }),\n})\n```\n\n### Role-Based Middleware\n```typescript\nconst requireRole = (role: string) =>\n  t.middleware(({ ctx, next }) => {\n    if (!ctx.session || ctx.session.role !== role) {\n      throw new TRPCError({ code: 'FORBIDDEN' })\n    }\n    return next()\n  })\n\nexport const adminProcedure = protectedProcedure.use(requireRole('admin'))\n\nexport const userRouter = router({\n  delete: adminProcedure\n    .input(z.string())\n    .mutation(({ input }) => {\n      return db.user.delete({ where: { id: input } })\n    }),\n})\n```\n\n## Client Usage\n\n### Queries\n```typescript\n'use client'\n\nimport { trpc } from '@\u002Flib\u002Ftrpc'\n\nexport default function UserList() {\n  \u002F\u002F Basic query\n  const users = trpc.user.list.useQuery()\n\n  \u002F\u002F Query with input\n  const user = trpc.user.getById.useQuery('user-123')\n\n  \u002F\u002F Disabled query\n  const profile = trpc.user.getProfile.useQuery(\n    { id: userId },\n    { enabled: !!userId }\n  )\n\n  \u002F\u002F With options\n  const posts = trpc.post.list.useQuery(undefined, {\n    refetchInterval: 5000,\n    staleTime: 1000,\n  })\n\n  if (users.isLoading) return \u003Cdiv>Loading...\u003C\u002Fdiv>\n  if (users.error) return \u003Cdiv>Error: {users.error.message}\u003C\u002Fdiv>\n\n  return (\n    \u003Cul>\n      {users.data?.map(user => (\n        \u003Cli key={user.id}>{user.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n```\n\n### Mutations\n```typescript\n'use client'\n\nexport default function CreateUser() {\n  const utils = trpc.useContext()\n\n  const createUser = trpc.user.create.useMutation({\n    onSuccess: () => {\n      \u002F\u002F Invalidate and refetch\n      utils.user.list.invalidate()\n    },\n    onError: (error) => {\n      console.error('Failed to create user:', error)\n    },\n  })\n\n  const handleSubmit = (e: React.FormEvent\u003CHTMLFormElement>) => {\n    e.preventDefault()\n    const formData = new FormData(e.currentTarget)\n\n    createUser.mutate({\n      name: formData.get('name') as string,\n      email: formData.get('email') as string,\n    })\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput name=\"name\" required \u002F>\n      \u003Cinput name=\"email\" type=\"email\" required \u002F>\n      \u003Cbutton type=\"submit\" disabled={createUser.isLoading}>\n        {createUser.isLoading ? 'Creating...' : 'Create User'}\n      \u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\n### Optimistic Updates\n```typescript\nconst updatePost = trpc.post.update.useMutation({\n  onMutate: async (newPost) => {\n    \u002F\u002F Cancel outgoing refetches\n    await utils.post.list.cancel()\n\n    \u002F\u002F Snapshot previous value\n    const previousPosts = utils.post.list.getData()\n\n    \u002F\u002F Optimistically update\n    utils.post.list.setData(undefined, (old) =>\n      old?.map(post =>\n        post.id === newPost.id ? { ...post, ...newPost } : post\n      )\n    )\n\n    return { previousPosts }\n  },\n  onError: (err, newPost, context) => {\n    \u002F\u002F Rollback on error\n    utils.post.list.setData(undefined, context?.previousPosts)\n  },\n  onSettled: () => {\n    \u002F\u002F Refetch after success or error\n    utils.post.list.invalidate()\n  },\n})\n```\n\n### Infinite Queries\n```typescript\n\u002F\u002F Server\nexport const postRouter = router({\n  list: publicProcedure\n    .input(z.object({\n      cursor: z.string().optional(),\n      limit: z.number().min(1).max(100).default(10),\n    }))\n    .query(async ({ input }) => {\n      const posts = await db.post.findMany({\n        take: input.limit + 1,\n        cursor: input.cursor ? { id: input.cursor } : undefined,\n      })\n\n      let nextCursor: string | undefined = undefined\n      if (posts.length > input.limit) {\n        const nextItem = posts.pop()\n        nextCursor = nextItem!.id\n      }\n\n      return { posts, nextCursor }\n    }),\n})\n\n\u002F\u002F Client\nexport default function InfinitePosts() {\n  const posts = trpc.post.list.useInfiniteQuery(\n    { limit: 10 },\n    {\n      getNextPageParam: (lastPage) => lastPage.nextCursor,\n    }\n  )\n\n  return (\n    \u003Cdiv>\n      {posts.data?.pages.map((page, i) => (\n        \u003Cdiv key={i}>\n          {page.posts.map(post => (\n            \u003Cdiv key={post.id}>{post.title}\u003C\u002Fdiv>\n          ))}\n        \u003C\u002Fdiv>\n      ))}\n\n      \u003Cbutton\n        onClick={() => posts.fetchNextPage()}\n        disabled={!posts.hasNextPage || posts.isFetchingNextPage}\n      >\n        {posts.isFetchingNextPage ? 'Loading...' : 'Load More'}\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n## Error Handling\n\n### Server Errors\n```typescript\nimport { TRPCError } from '@trpc\u002Fserver'\n\nexport const postRouter = router({\n  getById: publicProcedure\n    .input(z.string())\n    .query(async ({ input }) => {\n      const post = await db.post.findUnique({ where: { id: input } })\n\n      if (!post) {\n        throw new TRPCError({\n          code: 'NOT_FOUND',\n          message: 'Post not found',\n        })\n      }\n\n      return post\n    }),\n\n  create: protectedProcedure\n    .input(z.object({ title: z.string() }))\n    .mutation(async ({ ctx, input }) => {\n      if (!ctx.session.verified) {\n        throw new TRPCError({\n          code: 'FORBIDDEN',\n          message: 'Email must be verified',\n        })\n      }\n\n      try {\n        return await db.post.create({ data: input })\n      } catch (error) {\n        throw new TRPCError({\n          code: 'INTERNAL_SERVER_ERROR',\n          message: 'Failed to create post',\n          cause: error,\n        })\n      }\n    }),\n})\n```\n\n### Error Codes\n- `BAD_REQUEST` - Invalid input\n- `UNAUTHORIZED` - Not authenticated\n- `FORBIDDEN` - Not authorized\n- `NOT_FOUND` - Resource not found\n- `TIMEOUT` - Request timeout\n- `CONFLICT` - Resource conflict\n- `PRECONDITION_FAILED` - Precondition check failed\n- `PAYLOAD_TOO_LARGE` - Request too large\n- `METHOD_NOT_SUPPORTED` - HTTP method not supported\n- `TOO_MANY_REQUESTS` - Rate limited\n- `CLIENT_CLOSED_REQUEST` - Client closed request\n- `INTERNAL_SERVER_ERROR` - Server error\n\n### Client Error Handling\n```typescript\nconst createPost = trpc.post.create.useMutation({\n  onError: (error) => {\n    if (error.data?.code === 'UNAUTHORIZED') {\n      router.push('\u002Flogin')\n    } else if (error.data?.code === 'FORBIDDEN') {\n      alert('You do not have permission')\n    } else {\n      alert('Something went wrong')\n    }\n  },\n})\n```\n\n## Server-Side Calls\n\n### In Server Components\n```typescript\n\u002F\u002F app\u002Fusers\u002Fpage.tsx\nimport { createCaller } from '@\u002Fserver\u002Frouters\u002F_app'\nimport { createContext } from '@\u002Fserver\u002Fcontext'\n\nexport default async function UsersPage() {\n  const ctx = await createContext({ req: {} as any })\n  const caller = createCaller(ctx)\n\n  const users = await caller.user.list()\n\n  return (\n    \u003Cul>\n      {users.map(user => (\n        \u003Cli key={user.id}>{user.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n```\n\n### Create Caller\n```typescript\n\u002F\u002F server\u002Frouters\u002F_app.ts\nexport const createCaller = createCallerFactory(appRouter)\n\n\u002F\u002F Usage\nconst caller = createCaller(ctx)\nconst user = await caller.user.getById('123')\n```\n\n## Advanced Patterns\n\n### Request Batching\n```typescript\nimport { httpBatchLink } from '@trpc\u002Fclient'\n\nconst trpcClient = trpc.createClient({\n  links: [\n    httpBatchLink({\n      url: '\u002Fapi\u002Ftrpc',\n      maxURLLength: 2083, \u002F\u002F Reasonable limit\n    }),\n  ],\n})\n```\n\n### Request Deduplication\nAutomatic with React Query - multiple components requesting same data will only make one request.\n\n### Custom Headers\n```typescript\nconst trpcClient = trpc.createClient({\n  links: [\n    httpBatchLink({\n      url: '\u002Fapi\u002Ftrpc',\n      headers: () => {\n        return {\n          Authorization: `Bearer ${getToken()}`,\n        }\n      },\n    }),\n  ],\n})\n```\n\n### Error Formatting\n```typescript\n\u002F\u002F server\u002Ftrpc.ts\nconst t = initTRPC.context\u003CContext>().create({\n  errorFormatter({ shape, error }) {\n    return {\n      ...shape,\n      data: {\n        ...shape.data,\n        zodError:\n          error.cause instanceof ZodError\n            ? error.cause.flatten()\n            : null,\n      },\n    }\n  },\n})\n```\n\n## Testing\n\n### Testing Procedures\n```typescript\nimport { appRouter } from '@\u002Fserver\u002Frouters\u002F_app'\nimport { createCaller } from '@\u002Fserver\u002Frouters\u002F_app'\n\ndescribe('user router', () => {\n  it('creates user', async () => {\n    const ctx = { session: mockSession, db: mockDb }\n    const caller = createCaller(ctx)\n\n    const user = await caller.user.create({\n      name: 'John',\n      email: 'john@example.com',\n    })\n\n    expect(user.name).toBe('John')\n  })\n})\n```\n\n## Best Practices\n\n1. **Use Zod for validation** - Always validate inputs\n2. **Keep procedures small** - Single responsibility\n3. **Use middleware for auth** - Don't repeat auth checks\n4. **Type your context** - Full type safety\n5. **Organize routers** - Split into logical domains\n6. **Handle errors properly** - Use appropriate error codes\n7. **Leverage React Query** - Use its caching and refetching\n8. **Batch requests** - Enable batching for better performance\n9. **Use optimistic updates** - Better UX\n10. **Document procedures** - Add JSDoc comments\n\n## Resources\n\n- Docs: https:\u002F\u002Ftrpc.io\n- Next.js Guide: https:\u002F\u002Ftrpc.io\u002Fdocs\u002Fnextjs\n- Examples: https:\u002F\u002Fgithub.com\u002Ftrpc\u002Ftrpc\u002Ftree\u002Fmain\u002Fexamples\n","# tRPC\n\nExpert assistance with tRPC - End-to-end typesafe APIs with TypeScript.\n\n## Overview\n\ntRPC enables building fully typesafe APIs without schemas or code generation:\n- Full TypeScript inference from server to client\n- No code generation needed\n- Excellent DX with autocomplete and type safety\n- Works great with Next.js, React Query, and more\n\n## Quick Start\n\n### Installation\n```bash\n# Core packages\nnpm install @trpc\u002Fserver@next @trpc\u002Fclient@next @trpc\u002Freact-query@next\n\n# Peer dependencies\nnpm install @tanstack\u002Freact-query@latest zod\n```\n\n### Basic Setup (Next.js App Router)\n\n**1. Create tRPC Router**\n```typescript\n\u002F\u002F server\u002Ftrpc.ts\nimport { initTRPC } from '@trpc\u002Fserver'\nimport { z } from 'zod'\n\nconst t = initTRPC.create()\n\nexport const router = t.router\nexport const publicProcedure = t.procedure\n```\n\n**2. Define API Router**\n```typescript\n\u002F\u002F server\u002Frouters\u002F_app.ts\nimport { router, publicProcedure } from '..\u002Ftrpc'\nimport { z } from 'zod'\n\nexport const appRouter = router({\n  hello: publicProcedure\n    .input(z.object({ name: z.string() }))\n    .query(({ input }) => {\n      return { greeting: `Hello ${input.name}!` }\n    }),\n\n  createUser: publicProcedure\n    .input(z.object({\n      name: z.string(),\n      email: z.string().email(),\n    }))\n    .mutation(async ({ input }) => {\n      const user = await db.user.create({ data: input })\n      return user\n    }),\n})\n\nexport type AppRouter = typeof appRouter\n```\n\n**3. Create API Route**\n```typescript\n\u002F\u002F app\u002Fapi\u002Ftrpc\u002F[trpc]\u002Froute.ts\nimport { fetchRequestHandler } from '@trpc\u002Fserver\u002Fadapters\u002Ffetch'\nimport { appRouter } from '@\u002Fserver\u002Frouters\u002F_app'\n\nconst handler = (req: Request) =>\n  fetchRequestHandler({\n    endpoint: '\u002Fapi\u002Ftrpc',\n    req,\n    router: appRouter,\n    createContext: () => ({}),\n  })\n\nexport { handler as GET, handler as POST }\n```\n\n**4. Setup Client Provider**\n```typescript\n\u002F\u002F app\u002Fproviders.tsx\n'use client'\n\nimport { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query'\nimport { httpBatchLink } from '@trpc\u002Fclient'\nimport { useState } from 'react'\nimport { trpc } from '@\u002Flib\u002Ftrpc'\n\nexport function Providers({ children }: { children: React.ReactNode }) {\n  const [queryClient] = useState(() => new QueryClient())\n  const [trpcClient] = useState(() =>\n    trpc.createClient({\n      links: [\n        httpBatchLink({\n          url: 'http:\u002F\u002Flocalhost:3000\u002Fapi\u002Ftrpc',\n        }),\n      ],\n    })\n  )\n\n  return (\n    \u003Ctrpc.Provider client={trpcClient} queryClient={queryClient}>\n      \u003CQueryClientProvider client={queryClient}>\n        {children}\n      \u003C\u002FQueryClientProvider>\n    \u003C\u002Ftrpc.Provider>\n  )\n}\n```\n\n**5. Create tRPC Client**\n```typescript\n\u002F\u002F lib\u002Ftrpc.ts\nimport { createTRPCReact } from '@trpc\u002Freact-query'\nimport type { AppRouter } from '@\u002Fserver\u002Frouters\u002F_app'\n\nexport const trpc = createTRPCReact\u003CAppRouter>()\n```\n\n**6. Use in Components**\n```typescript\n'use client'\n\nimport { trpc } from '@\u002Flib\u002Ftrpc'\n\nexport default function Home() {\n  const hello = trpc.hello.useQuery({ name: 'World' })\n  const createUser = trpc.createUser.useMutation()\n\n  return (\n    \u003Cdiv>\n      \u003Cp>{hello.data?.greeting}\u003C\u002Fp>\n      \u003Cbutton\n        onClick={() => createUser.mutate({\n          name: 'John',\n          email: 'john@example.com'\n        })}\n      >\n        Create User\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n## Router Definition\n\n### Basic Router\n```typescript\nimport { router, publicProcedure } from '.\u002Ftrpc'\nimport { z } from 'zod'\n\nexport const userRouter = router({\n  \u002F\u002F Query - for fetching data\n  getById: publicProcedure\n    .input(z.string())\n    .query(async ({ input }) => {\n      return await db.user.findUnique({ where: { id: input } })\n    }),\n\n  \u002F\u002F Mutation - for creating\u002Fupdating\u002Fdeleting\n  create: publicProcedure\n    .input(z.object({\n      name: z.string(),\n      email: z.string().email(),\n    }))\n    .mutation(async ({ input }) => {\n      return await db.user.create({ data: input })\n    }),\n\n  \u002F\u002F Subscription - for real-time updates\n  onUpdate: publicProcedure\n    .subscription(() => {\n      return observable\u003CUser>((emit) => {\n        \u002F\u002F Implementation\n      })\n    }),\n})\n```\n\n### Nested Routers\n```typescript\nimport { router } from '.\u002Ftrpc'\nimport { userRouter } from '.\u002Frouters\u002Fuser'\nimport { postRouter } from '.\u002Frouters\u002Fpost'\nimport { commentRouter } from '.\u002Frouters\u002Fcomment'\n\nexport const appRouter = router({\n  user: userRouter,\n  post: postRouter,\n  comment: commentRouter,\n})\n\n\u002F\u002F Usage on client:\n\u002F\u002F trpc.user.getById.useQuery('123')\n\u002F\u002F trpc.post.list.useQuery()\n\u002F\u002F trpc.comment.create.useMutation()\n```\n\n### Merging Routers\n```typescript\nimport { router, publicProcedure } from '.\u002Ftrpc'\n\nconst userRouter = router({\n  list: publicProcedure.query(() => {\u002F* ... *\u002F}),\n  getById: publicProcedure.input(z.string()).query(() => {\u002F* ... *\u002F}),\n})\n\nconst postRouter = router({\n  list: publicProcedure.query(() => {\u002F* ... *\u002F}),\n  create: publicProcedure.input(z.object({})).mutation(() => {\u002F* ... *\u002F}),\n})\n\n\u002F\u002F Merge into app router\nexport const appRouter = router({\n  user: userRouter,\n  post: postRouter,\n})\n```\n\n## Input Validation with Zod\n\n### Basic Validation\n```typescript\nimport { z } from 'zod'\n\nexport const userRouter = router({\n  create: publicProcedure\n    .input(z.object({\n      name: z.string().min(2).max(50),\n      email: z.string().email(),\n      age: z.number().int().positive().optional(),\n      role: z.enum(['user', 'admin']),\n    }))\n    .mutation(async ({ input }) => {\n      \u002F\u002F input is fully typed!\n      return await db.user.create({ data: input })\n    }),\n})\n```\n\n### Complex Validation\n```typescript\nconst createPostInput = z.object({\n  title: z.string().min(5).max(100),\n  content: z.string().min(10),\n  published: z.boolean().default(false),\n  tags: z.array(z.string()).min(1).max(5),\n  metadata: z.object({\n    views: z.number().default(0),\n    likes: z.number().default(0),\n  }).optional(),\n})\n\nexport const postRouter = router({\n  create: publicProcedure\n    .input(createPostInput)\n    .mutation(async ({ input }) => {\n      return await db.post.create({ data: input })\n    }),\n})\n```\n\n### Reusable Schemas\n```typescript\n\u002F\u002F schemas\u002Fuser.ts\nexport const userSchema = z.object({\n  id: z.string(),\n  name: z.string(),\n  email: z.string().email(),\n})\n\nexport const createUserSchema = userSchema.omit({ id: true })\nexport const updateUserSchema = userSchema.partial()\n\n\u002F\u002F Use in router\nexport const userRouter = router({\n  create: publicProcedure\n    .input(createUserSchema)\n    .mutation(({ input }) => {\u002F* ... *\u002F}),\n\n  update: publicProcedure\n    .input(z.object({\n      id: z.string(),\n      data: updateUserSchema,\n    }))\n    .mutation(({ input }) => {\u002F* ... *\u002F}),\n})\n```\n\n## Context\n\n### Creating Context\n```typescript\n\u002F\u002F server\u002Fcontext.ts\nimport { inferAsyncReturnType } from '@trpc\u002Fserver'\nimport { FetchCreateContextFnOptions } from '@trpc\u002Fserver\u002Fadapters\u002Ffetch'\n\nexport async function createContext(opts: FetchCreateContextFnOptions) {\n  \u002F\u002F Get session from cookies\u002Fheaders\n  const session = await getSession(opts.req)\n\n  return {\n    session,\n    db,\n  }\n}\n\nexport type Context = inferAsyncReturnType\u003Ctypeof createContext>\n```\n\n### Using Context in tRPC\n```typescript\n\u002F\u002F server\u002Ftrpc.ts\nimport { initTRPC } from '@trpc\u002Fserver'\nimport { Context } from '.\u002Fcontext'\n\nconst t = initTRPC.context\u003CContext>().create()\n\nexport const router = t.router\nexport const publicProcedure = t.procedure\n```\n\n### Accessing Context in Procedures\n```typescript\nexport const userRouter = router({\n  me: publicProcedure.query(({ ctx }) => {\n    \u002F\u002F ctx.session, ctx.db are available\n    if (!ctx.session) {\n      throw new TRPCError({ code: 'UNAUTHORIZED' })\n    }\n\n    return ctx.db.user.findUnique({\n      where: { id: ctx.session.userId }\n    })\n  }),\n})\n```\n\n## Middleware\n\n### Creating Middleware\n```typescript\n\u002F\u002F server\u002Ftrpc.ts\nimport { initTRPC, TRPCError } from '@trpc\u002Fserver'\n\nconst t = initTRPC.context\u003CContext>().create()\n\n\u002F\u002F Logging middleware\nconst loggerMiddleware = t.middleware(async ({ path, type, next }) => {\n  const start = Date.now()\n  const result = await next()\n  const duration = Date.now() - start\n\n  console.log(`${type} ${path} took ${duration}ms`)\n\n  return result\n})\n\n\u002F\u002F Auth middleware\nconst isAuthed = t.middleware(({ ctx, next }) => {\n  if (!ctx.session) {\n    throw new TRPCError({ code: 'UNAUTHORIZED' })\n  }\n\n  return next({\n    ctx: {\n      \u002F\u002F Infers session is non-nullable\n      session: ctx.session,\n    },\n  })\n})\n\n\u002F\u002F Create procedures with middleware\nexport const publicProcedure = t.procedure.use(loggerMiddleware)\nexport const protectedProcedure = t.procedure.use(loggerMiddleware).use(isAuthed)\n```\n\n### Using Protected Procedures\n```typescript\nexport const postRouter = router({\n  \u002F\u002F Public - anyone can access\n  list: publicProcedure.query(() => {\n    return db.post.findMany({ where: { published: true } })\n  }),\n\n  \u002F\u002F Protected - requires authentication\n  create: protectedProcedure\n    .input(z.object({ title: z.string() }))\n    .mutation(({ ctx, input }) => {\n      \u002F\u002F ctx.session is guaranteed to exist\n      return db.post.create({\n        data: {\n          ...input,\n          authorId: ctx.session.userId,\n        },\n      })\n    }),\n})\n```\n\n### Role-Based Middleware\n```typescript\nconst requireRole = (role: string) =>\n  t.middleware(({ ctx, next }) => {\n    if (!ctx.session || ctx.session.role !== role) {\n      throw new TRPCError({ code: 'FORBIDDEN' })\n    }\n    return next()\n  })\n\nexport const adminProcedure = protectedProcedure.use(requireRole('admin'))\n\nexport const userRouter = router({\n  delete: adminProcedure\n    .input(z.string())\n    .mutation(({ input }) => {\n      return db.user.delete({ where: { id: input } })\n    }),\n})\n```\n\n## Client Usage\n\n### Queries\n```typescript\n'use client'\n\nimport { trpc } from '@\u002Flib\u002Ftrpc'\n\nexport default function UserList() {\n  \u002F\u002F Basic query\n  const users = trpc.user.list.useQuery()\n\n  \u002F\u002F Query with input\n  const user = trpc.user.getById.useQuery('user-123')\n\n  \u002F\u002F Disabled query\n  const profile = trpc.user.getProfile.useQuery(\n    { id: userId },\n    { enabled: !!userId }\n  )\n\n  \u002F\u002F With options\n  const posts = trpc.post.list.useQuery(undefined, {\n    refetchInterval: 5000,\n    staleTime: 1000,\n  })\n\n  if (users.isLoading) return \u003Cdiv>Loading...\u003C\u002Fdiv>\n  if (users.error) return \u003Cdiv>Error: {users.error.message}\u003C\u002Fdiv>\n\n  return (\n    \u003Cul>\n      {users.data?.map(user => (\n        \u003Cli key={user.id}>{user.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n```\n\n### Mutations\n```typescript\n'use client'\n\nexport default function CreateUser() {\n  const utils = trpc.useContext()\n\n  const createUser = trpc.user.create.useMutation({\n    onSuccess: () => {\n      \u002F\u002F Invalidate and refetch\n      utils.user.list.invalidate()\n    },\n    onError: (error) => {\n      console.error('Failed to create user:', error)\n    },\n  })\n\n  const handleSubmit = (e: React.FormEvent\u003CHTMLFormElement>) => {\n    e.preventDefault()\n    const formData = new FormData(e.currentTarget)\n\n    createUser.mutate({\n      name: formData.get('name') as string,\n      email: formData.get('email') as string,\n    })\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003Cinput name=\"name\" required \u002F>\n      \u003Cinput name=\"email\" type=\"email\" required \u002F>\n      \u003Cbutton type=\"submit\" disabled={createUser.isLoading}>\n        {createUser.isLoading ? 'Creating...' : 'Create User'}\n      \u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\n### Optimistic Updates\n```typescript\nconst updatePost = trpc.post.update.useMutation({\n  onMutate: async (newPost) => {\n    \u002F\u002F Cancel outgoing refetches\n    await utils.post.list.cancel()\n\n    \u002F\u002F Snapshot previous value\n    const previousPosts = utils.post.list.getData()\n\n    \u002F\u002F Optimistically update\n    utils.post.list.setData(undefined, (old) =>\n      old?.map(post =>\n        post.id === newPost.id ? { ...post, ...newPost } : post\n      )\n    )\n\n    return { previousPosts }\n  },\n  onError: (err, newPost, context) => {\n    \u002F\u002F Rollback on error\n    utils.post.list.setData(undefined, context?.previousPosts)\n  },\n  onSettled: () => {\n    \u002F\u002F Refetch after success or error\n    utils.post.list.invalidate()\n  },\n})\n```\n\n### Infinite Queries\n```typescript\n\u002F\u002F Server\nexport const postRouter = router({\n  list: publicProcedure\n    .input(z.object({\n      cursor: z.string().optional(),\n      limit: z.number().min(1).max(100).default(10),\n    }))\n    .query(async ({ input }) => {\n      const posts = await db.post.findMany({\n        take: input.limit + 1,\n        cursor: input.cursor ? { id: input.cursor } : undefined,\n      })\n\n      let nextCursor: string | undefined = undefined\n      if (posts.length > input.limit) {\n        const nextItem = posts.pop()\n        nextCursor = nextItem!.id\n      }\n\n      return { posts, nextCursor }\n    }),\n})\n\n\u002F\u002F Client\nexport default function InfinitePosts() {\n  const posts = trpc.post.list.useInfiniteQuery(\n    { limit: 10 },\n    {\n      getNextPageParam: (lastPage) => lastPage.nextCursor,\n    }\n  )\n\n  return (\n    \u003Cdiv>\n      {posts.data?.pages.map((page, i) => (\n        \u003Cdiv key={i}>\n          {page.posts.map(post => (\n            \u003Cdiv key={post.id}>{post.title}\u003C\u002Fdiv>\n          ))}\n        \u003C\u002Fdiv>\n      ))}\n\n      \u003Cbutton\n        onClick={() => posts.fetchNextPage()}\n        disabled={!posts.hasNextPage || posts.isFetchingNextPage}\n      >\n        {posts.isFetchingNextPage ? 'Loading...' : 'Load More'}\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\n## Error Handling\n\n### Server Errors\n```typescript\nimport { TRPCError } from '@trpc\u002Fserver'\n\nexport const postRouter = router({\n  getById: publicProcedure\n    .input(z.string())\n    .query(async ({ input }) => {\n      const post = await db.post.findUnique({ where: { id: input } })\n\n      if (!post) {\n        throw new TRPCError({\n          code: 'NOT_FOUND',\n          message: 'Post not found',\n        })\n      }\n\n      return post\n    }),\n\n  create: protectedProcedure\n    .input(z.object({ title: z.string() }))\n    .mutation(async ({ ctx, input }) => {\n      if (!ctx.session.verified) {\n        throw new TRPCError({\n          code: 'FORBIDDEN',\n          message: 'Email must be verified',\n        })\n      }\n\n      try {\n        return await db.post.create({ data: input })\n      } catch (error) {\n        throw new TRPCError({\n          code: 'INTERNAL_SERVER_ERROR',\n          message: 'Failed to create post',\n          cause: error,\n        })\n      }\n    }),\n})\n```\n\n### Error Codes\n- `BAD_REQUEST` - Invalid input\n- `UNAUTHORIZED` - Not authenticated\n- `FORBIDDEN` - Not authorized\n- `NOT_FOUND` - Resource not found\n- `TIMEOUT` - Request timeout\n- `CONFLICT` - Resource conflict\n- `PRECONDITION_FAILED` - Precondition check failed\n- `PAYLOAD_TOO_LARGE` - Request too large\n- `METHOD_NOT_SUPPORTED` - HTTP method not supported\n- `TOO_MANY_REQUESTS` - Rate limited\n- `CLIENT_CLOSED_REQUEST` - Client closed request\n- `INTERNAL_SERVER_ERROR` - Server error\n\n### Client Error Handling\n```typescript\nconst createPost = trpc.post.create.useMutation({\n  onError: (error) => {\n    if (error.data?.code === 'UNAUTHORIZED') {\n      router.push('\u002Flogin')\n    } else if (error.data?.code === 'FORBIDDEN') {\n      alert('You do not have permission')\n    } else {\n      alert('Something went wrong')\n    }\n  },\n})\n```\n\n## Server-Side Calls\n\n### In Server Components\n```typescript\n\u002F\u002F app\u002Fusers\u002Fpage.tsx\nimport { createCaller } from '@\u002Fserver\u002Frouters\u002F_app'\nimport { createContext } from '@\u002Fserver\u002Fcontext'\n\nexport default async function UsersPage() {\n  const ctx = await createContext({ req: {} as any })\n  const caller = createCaller(ctx)\n\n  const users = await caller.user.list()\n\n  return (\n    \u003Cul>\n      {users.map(user => (\n        \u003Cli key={user.id}>{user.name}\u003C\u002Fli>\n      ))}\n    \u003C\u002Ful>\n  )\n}\n```\n\n### Create Caller\n```typescript\n\u002F\u002F server\u002Frouters\u002F_app.ts\nexport const createCaller = createCallerFactory(appRouter)\n\n\u002F\u002F Usage\nconst caller = createCaller(ctx)\nconst user = await caller.user.getById('123')\n```\n\n## Advanced Patterns\n\n### Request Batching\n```typescript\nimport { httpBatchLink } from '@trpc\u002Fclient'\n\nconst trpcClient = trpc.createClient({\n  links: [\n    httpBatchLink({\n      url: '\u002Fapi\u002Ftrpc',\n      maxURLLength: 2083, \u002F\u002F Reasonable limit\n    }),\n  ],\n})\n```\n\n### Request Deduplication\nAutomatic with React Query - multiple components requesting same data will only make one request.\n\n### Custom Headers\n```typescript\nconst trpcClient = trpc.createClient({\n  links: [\n    httpBatchLink({\n      url: '\u002Fapi\u002Ftrpc',\n      headers: () => {\n        return {\n          Authorization: `Bearer ${getToken()}`,\n        }\n      },\n    }),\n  ],\n})\n```\n\n### Error Formatting\n```typescript\n\u002F\u002F server\u002Ftrpc.ts\nconst t = initTRPC.context\u003CContext>().create({\n  errorFormatter({ shape, error }) {\n    return {\n      ...shape,\n      data: {\n        ...shape.data,\n        zodError:\n          error.cause instanceof ZodError\n            ? error.cause.flatten()\n            : null,\n      },\n    }\n  },\n})\n```\n\n## Testing\n\n### Testing Procedures\n```typescript\nimport { appRouter } from '@\u002Fserver\u002Frouters\u002F_app'\nimport { createCaller } from '@\u002Fserver\u002Frouters\u002F_app'\n\ndescribe('user router', () => {\n  it('creates user', async () => {\n    const ctx = { session: mockSession, db: mockDb }\n    const caller = createCaller(ctx)\n\n    const user = await caller.user.create({\n      name: 'John',\n      email: 'john@example.com',\n    })\n\n    expect(user.name).toBe('John')\n  })\n})\n```\n\n## Best Practices\n\n1. **Use Zod for validation** - Always validate inputs\n2. **Keep procedures small** - Single responsibility\n3. **Use middleware for auth** - Don't repeat auth checks\n4. **Type your context** - Full type safety\n5. **Organize routers** - Split into logical domains\n6. **Handle errors properly** - Use appropriate error codes\n7. **Leverage React Query** - Use its caching and refetching\n8. **Batch requests** - Enable batching for better performance\n9. **Use optimistic updates** - Better UX\n10. **Document procedures** - Add JSDoc comments\n\n## Resources\n\n- Docs: https:\u002F\u002Ftrpc.io\n- Next.js Guide: https:\u002F\u002Ftrpc.io\u002Fdocs\u002Fnextjs\n- Examples: https:\u002F\u002Fgithub.com\u002Ftrpc\u002Ftrpc\u002Ftree\u002Fmain\u002Fexamples\n",{"name":5,"description":7},"main","e7e336ac628c3e3bd771aff1aaa469c93778f94a","skills\u002Fifoster01\u002Ftrpc-best-practices",0,"https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del","2026-08-23 22:39:37",{"source":8,"skillName":5,"commands":51,"prompt":61,"zipAvailable":62},[52,55,58],{"tool":53,"command":54},"npx","npx skills add kbarbel640-del\u002Fskills --skill tRPC",{"tool":56,"command":57},"bunx","bunx skills add kbarbel640-del\u002Fskills --skill tRPC",{"tool":59,"command":60},"pnpm","pnpm dlx skills add kbarbel640-del\u002Fskills --skill tRPC","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fifoster01\u002Ftrpc-best-practices\u002FSKILL.md",true,[64],{"path":65,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":68,"sourceHtmlUrl":69},"_meta.json","REFERENCE","参考文档",293,"https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fifoster01\u002Ftrpc-best-practices\u002F_meta.json",[71,101,129,150,156,176],{"skillNo":72,"name":73,"nameZh":6,"description":74,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":75,"categories":76,"keywords":82,"publishedAt":38},"01M6G8J55967D6281ACMKAKTEQ","crowd-prompting","A marketplace where AI agents improve prompts, system instructions, tool descriptions, and other text-based content with domain expertise from real-world operations — and earn tokens for valuable contributions.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzzadrian\u002Fcrowd-prompting\u002FSKILL.md",[77],{"categoryNo":78,"categoryName":79,"taxonomyType":80,"taxonomyTypeDesc":81},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[83,84,85,20,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100],"crowd prompting","prompting","api","tokens","domain","world","prompts","operations","instructions","text","tool","system","improve","productivity","marketplace","https","content","ai agents",{"skillNo":102,"name":103,"nameZh":6,"description":104,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":105,"categories":106,"keywords":112,"publishedAt":38},"01M6G8J0PKPAYVET6BWE8W4C26","dilemm-ai","Compete in DilemmAI, the prisoner's dilemma AI arena at dilemm.ai. Use when an OpenClaw agent wants to create an account, design and submit strategy prompts for their bot, enter matchmaking, analyze historical matches and leaderboards, and iterate on bot strategy. Requires headless browser automation (no API available yet).","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzyori\u002Fdilemm-ai\u002FSKILL.md",[107],{"categoryNo":108,"categoryName":109,"taxonomyType":110,"taxonomyTypeDesc":111},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[113,85,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,89,31],"dilemm ai","arena","analyze","browser","openclaw agent","create","automation","browser automation","bot","openclaw","design","strategy","headless","compete","submit","matchmaking",{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":138,"publishedAt":38},"01M6G8HWAF3K9SW7NP3Z83CPJ6","arxiv-paper-reviews","Interact with arXiv Crawler API to fetch papers, read reviews, and submit comments. Use when working with arXiv papers, fetching paper lists by date\u002Fcategory\u002Finterest, viewing paper details with comments, or submitting paper reviews via API at http:\u002F\u002F150.158.152.82:8000.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Fweak-accept\u002FSKILL.md",[135],{"categoryNo":136,"categoryName":137,"taxonomyType":110,"taxonomyTypeDesc":111},"01JBSEEDCAT000000000000021","资料检索与研究",[139,140,141,142,143,144,145,20,146,127,31,147,85,148,149],"arxiv paper reviews","reviews","arxiv","paper","crawler","papers","comments","fetch","read","http","via",{"skillNo":151,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":155,"publishedAt":38},"01M6G8HQJ7N7WMY6YV7D7SSJ16","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzxrys\u002Farxiv-paper-reviews\u002FSKILL.md",[154],{"categoryNo":136,"categoryName":137,"taxonomyType":110,"taxonomyTypeDesc":111},[142,141,140,139,149,144,145,20,143,127,31,146,147,85,148],{"skillNo":157,"name":158,"nameZh":6,"description":159,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":160,"categories":161,"keywords":162,"publishedAt":38},"01M6G8HM95Y64JNVFC2HN4E2BG","git-summary","Get a quick summary of the current Git repository including status, recent commits, branches, and contributors.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzweack\u002Fgit-summary\u002FSKILL.md",[],[163,164,165,166,167,168,169,170,171,172,173,174,175],"git","summary","git summary","commits","repository","win32","status","linux","contributors","true","quick","get","branches",{"skillNo":177,"name":178,"nameZh":6,"description":179,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":180,"categories":181,"keywords":186,"publishedAt":38},"01M6G8HGEGF05CNEJCGVYFSC4R","agent-constitution","Interact with AgentConstitution governance contracts on Base Sepolia. Check compliance, read rules, log actions, query governance state.","https:\u002F\u002Fgithub.com\u002Fkbarbel640-del\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fztsalexey\u002Fagent-constitution\u002FSKILL.md",[182,183],{"categoryNo":108,"categoryName":109,"taxonomyType":110,"taxonomyTypeDesc":111},{"categoryNo":184,"categoryName":185,"taxonomyType":80,"taxonomyTypeDesc":81},"01JBSEEDCAT000000000000037","律师与法务",[187,188,189,190,20,191,192,43,193,147,194,195,196,197,198,98,199,200,201],"agent constitution","constitution","contracts","tree","query","base","log","check","Rules","projects","compliance","actions","state","governance","github"]