Skip to content
Development
Skill

/web-data-fetching-trpc

tRPC type-safe API patterns — routers and procedures, input validation, context and middleware, TRPCError, and the typed client bridge

From plugin
agents-inc-skills
24200 skills
Install
$ 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.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/web-data-fetching-trpc

Context preview

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

tRPC type-safe API patterns — routers and procedures, input validation, context and middleware, TRPCError, and the typed client bridge

SKILL.md

web-data-fetching-trpc.SKILL.md
name: web-data-fetching-trpc
description: tRPC type-safe API patterns — routers and procedures, input validation, context and middleware, TRPCError, and the typed client bridge

tRPC Patterns

> **Quick Guide:** tRPC carries types from server to client through one exported type rather than > through a generated schema, so `export type AppRouter = typeof appRouter` is the whole bridge and > everything downstream fails without it. Procedures take a validated input and return a value; > `TRPCError` codes are what become HTTP statuses; middleware narrows the context type so an > authenticated procedure's `ctx.user` is non-nullable. In v11 the transformer moved inside the > link, subscriptions are async generators, and `@trpc/tanstack-react-query` is the current React > integration.

**Detailed Resources:**

  • [examples/core.md](examples/core.md) — initialization, context, a CRUD router, the provider, inferred types, `queryOptions`
  • [examples/middleware.md](examples/middleware.md) — logging, rate limiting, resource-scoped access
  • [examples/infinite-queries.md](examples/infinite-queries.md) — cursor pagination end to end
  • [examples/optimistic-updates.md](examples/optimistic-updates.md) — the full snapshot-and-rollback cycle
  • [examples/subscriptions.md](examples/subscriptions.md) — async generator subscriptions with resumable event ids
  • [examples/file-uploads.md](examples/file-uploads.md) — `File` in an input schema (v11+)
  • [reference.md](reference.md) — error code to HTTP status table, batching and invalidation notes, v10 → v11 migration

---

Which path applies

  • **`@trpc/tanstack-react-query`** — the current integration. `createTRPCContext` yields a `useTRPC`

hook, and each procedure exposes `queryOptions()`, `mutationOptions()`, `infiniteQueryOptions()` and `queryKey()` that go straight into the standard query hooks. Pattern 5.

  • **`@trpc/react-query`** — the classic integration, still supported in v11. Procedures carry their

own `trpc.x.useQuery()` hooks instead, and a cache key comes from `getQueryKey(trpc.x)` rather than from a `queryKey()` on the procedure. Migrate when convenient; the two can coexist.

---

<critical_requirements>

Before writing tRPC code

**Export the router's type: `export type AppRouter = typeof appRouter`.** That single line is the whole client-side contract — without it the client falls back to `unknown` and every guarantee tRPC offers is gone, with no error at the point the export was forgotten.

**Give every procedure that accepts input a validator on `.input()`.** It is both the runtime check and the source of the handler's parameter type, so a procedure without one receives `unknown` and tempts a cast.

**Throw `TRPCError` with a code rather than a bare `Error`.** The code is what maps to an HTTP status and what the client switches on; a bare `Error` arrives as an opaque 500.

**Place the transformer inside `httpBatchLink()`, not on `createTRPCClient()`.** v11 moved it, and the old position raises an error at client construction.

</critical_requirements>

---

**Auto-detection:** `initTRPC`, `createTRPCClient`, `createTRPCContext`, `createTRPCOptionsProxy`, `@trpc/server`, `@trpc/client`, `@trpc/react-query`, `@trpc/tanstack-react-query`, `TRPCError`, `publicProcedure`, `protectedProcedure`, `httpBatchLink`, `httpSubscriptionLink`, `loggerLink`, `inferRouterInputs`, `inferRouterOutputs`, `useTRPC`, `tracked`, `AppRouter`

**Applies to:**

  • Router and procedure definition, and composing routers into one
  • Input validation and the types inferred from it
  • Per-request context, and middleware that narrows it
  • Error codes and the shape the client receives
  • Turning a procedure into typed query and mutation options
  • Subscriptions, file inputs and cursor pagination

**Handled elsewhere:**

  • APIs published for third-party or non-TypeScript consumers, which want a language-neutral contract

document rather than a shared type

  • Caching policy, retries and invalidation semantics — this skill settles how a procedure becomes

the options a query client consumes, and the client decides what to do with them

  • The schema library used on `.input()`; any validator the version supports works, and the examples

show one

  • Session and token issuance; context consumes a session rather than establishing one

---

<philosophy>

Philosophy

There is no API description anywhere — no schema file, no generated client, no build step between the two halves. The router's inferred type _is_ the contract, and it is shared by importing a type across the codebase.

That buys immediate accuracy: a procedure's return type changes and every call site reddens in the same typecheck, with no regeneration step to forget. It costs polyglot support, a published contract, and HTTP caching — calls go out as POST by default, so a CDN in front of the endpoint has nothing to cache. Which is why tRPC suits an internal API in one TypeScript codebase and suits a public one badly.

</philosophy>

---

<patterns>

Core patterns

Pattern 1: Initialization

Initialize once per application and export the factories the routers compose from.

const t = initTRPC.context<Context>().create({
  transformer: superjson, // Date, Map and Set survive the wire
  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;

The error formatter is what turns a validation failure into something a form can render per field, instead of one message.

Full code: [examples/core.md](examples/core.md) — including the per-request context factory

---

Pattern 2: Procedures and input validation

const createUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1).
Read more
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

Other skills on agents-inc-skills.