Skip to content

/web-data-fetching-trpc

tRPC type-safe API patterns, procedures, middleware, React Query integration

shell
$ npx -y skills add agents-inc/skills --skill web-data-fetching-trpc --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/web-data-fetching-trpc
How auto-invocation works

Context preview

The summary Claude sees to decide when to auto-load this skill.

tRPC type-safe API patterns, procedures, middleware, React Query integration

SKILL.md

web-data-fetching-trpc.SKILL.md
name: web-data-fetching-trpc
description: tRPC type-safe API patterns, procedures, middleware, React Query integration

tRPC Type-Safe API Patterns

> **Quick Guide:** tRPC provides end-to-end type safety by sharing TypeScript types directly from server to client -- no code generation, no schema files. Export `AppRouter` type from your router (this is the key bridge). Use Zod for input validation, `TRPCError` with proper codes for errors, and middleware for auth. v11 is the current stable version: transformer goes inside `httpBatchLink()`, subscriptions use async generators (not `observable()`), and `@trpc/tanstack-react-query` is the recommended React integration.

---

<critical_requirements>

CRITICAL: Before Using This Skill

**(You MUST export `AppRouter` type from your tRPC router for client-side type inference)**

**(You MUST use `TRPCError` with appropriate error codes -- never throw raw Error objects)**

**(You MUST use Zod for input validation on ALL procedures accepting user input)**

**(You MUST place transformer inside `httpBatchLink()` in v11 -- NOT at client level)**

</critical_requirements>

---

**Auto-detection:** tRPC router, initTRPC, createTRPCClient, createTRPCContext, @trpc/server, @trpc/client, @trpc/react-query, @trpc/tanstack-react-query, TRPCError, procedure, publicProcedure, protectedProcedure, query, mutation, subscription, httpBatchLink, queryOptions, mutationOptions, useTRPC

**When to use:**

  • Building APIs in TypeScript monorepos with shared types
  • End-to-end type safety without code generation
  • Full-stack TypeScript applications where both client and server are TypeScript
  • Projects where types should flow automatically from backend to frontend

**When NOT to use:**

  • Public APIs consumed by third parties (use OpenAPI/REST)
  • Non-TypeScript clients (mobile apps, other languages)
  • Need HTTP caching at CDN level (tRPC uses POST by default)
  • GraphQL requirements with partial queries

**Key patterns covered:**

  • Router and procedure definition (initTRPC, router, procedure)
  • Input validation with Zod schemas
  • Context and middleware for authentication
  • Error handling with TRPCError codes
  • React integration via `@trpc/tanstack-react-query` (recommended) or `@trpc/react-query` (classic)
  • Optimistic updates, infinite queries, subscriptions

**Detailed Resources:**

  • [examples/core.md](examples/core.md) - Router setup, CRUD, provider, type inference, queryOptions
  • [examples/middleware.md](examples/middleware.md) - Logging, rate limiting, org-scoped access
  • [examples/infinite-queries.md](examples/infinite-queries.md) - Cursor pagination, infinite scroll
  • [examples/optimistic-updates.md](examples/optimistic-updates.md) - Optimistic updates with rollback
  • [examples/subscriptions.md](examples/subscriptions.md) - Async generator subscriptions, SSE
  • [examples/file-uploads.md](examples/file-uploads.md) - FormData file uploads (v11+)
  • [reference.md](reference.md) - Decision frameworks, error codes, anti-patterns, v11 migration

---

<philosophy>

Philosophy

tRPC eliminates API layer friction by sharing types directly between server and client. No schemas to write, no code to generate -- export your router type and import it client-side for full autocompletion and type safety.

**Core principles:**

  • **Zero schema duplication**: Types flow from backend to frontend automatically
  • **TypeScript-native**: Leverages TypeScript's type inference, not code generation
  • **Procedure-based**: Queries read data, mutations write data -- clear separation
  • **Composable middleware**: Build reusable authentication and validation layers
  • **Built on TanStack Query**: Full caching, invalidation, and optimistic updates via React Query

**Trade-offs:**

  • Requires TypeScript on both ends (no polyglot support)
  • Best in monorepos where types can be shared directly
  • Not suitable for public APIs needing OpenAPI documentation
  • Uses POST by default -- no HTTP caching without configuration

</philosophy>

---

<patterns>

Core Patterns

Pattern 1: tRPC Initialization and Router Setup

Initialize tRPC once per application. Export the router and procedure factories.

import { initTRPC, TRPCError } from "@trpc/server";
import { ZodError } from "zod";
import type { Context } from "./context";

const t = initTRPC.context<Context>().create({
  errorFormatter({ shape, error }) {
    return {
      ...shape,
      data: {
        ...shape.data,
        zodError:
          error.cause instanceof ZodError ? error.cause.flatten() : null,
      },
    };
  },
});

export const router = t.router;
export const publicProcedure = t.procedure;
export const middleware = t.middleware;

**Why good:** Single initialization point, error formatter provides structured Zod errors to client, exported factories enable composition across router files

See [examples/core.md](examples/core.md) Pattern 1 for complete router and context factory.

---

Pattern 2: Procedures with Zod Input Validation

Zod schemas provide runtime validation AND TypeScript inference from a single source.

const createUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1).max(100),
});

export const userRouter = router({
  create: protectedProcedure
    .input(createUserSchema)
    .mutation(async ({ input, ctx }) => {
      // input is typed: { email: string; name: string }
      return ctx.db.user.create({ data: input });
    }),
});
// BAD: No input validation -- input is 'unknown'
publicProcedure.mutation(async ({ input }) => {
  return ctx.db.user.create({ data: input as any }); // Dangerous!
});

**Why bad:** Without Zod validation, input is unknown type, no runtime validation, injection risks, `as any` defeats TypeScript

See [examples/core.md](examples/core.md) Pattern 2 for complete CRUD router.

---

Pattern 3: Authentication Middleware

Middleware narrows context types -- `ctx.user` becomes non-nullable afte

Read more
Read it on GitHub ↗

Showing the first part of this file.

Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin, auto-invoked