tRPC mit Claude Code: End-to-End-Typsicherheit ohne Code-Generation 2026

tRPC eliminiert die Lücke zwischen Backend und Frontend — vollständige TypeScript-Typsicherheit ohne Schema-Dateien, ohne Code-Generation. Claude Code kennt tRPC in- und auswendig: Router-Design, Middleware, React Query Integration und Next.js App Router.

Warum tRPC 2026 der Standard für TypeScript-Projekte ist

REST-APIs mit manuell gepflegten Types auf Client und Server waren jahrelang der Status quo — mit dem unvermeidlichen Ergebnis: Typen divergieren, Bugs entstehen an der Grenze zwischen Frontend und Backend. GraphQL löste dieses Problem mit Code-Generation, schuf aber neue Komplexität. tRPC geht einen radikal anderen Weg: Der Router-Typ selbst ist das Schema. Kein Codegen-Schritt, keine .graphql-Dateien, keine OpenAPI-Spezifikation — TypeScript-Inferenz erledigt alles.

Claude Code hat sich 2026 als idealer Partner für tRPC-Projekte etabliert. Die KI versteht den gesamten Stack — von der Procedure-Definition auf dem Server bis zum useQuery-Hook im React-Component — und kann Typen, Validierung und Integration in einem Schritt aufsetzen.

tRPC Kernprinzip: Der TypeScript-Typ des Routers wird direkt auf den Client übertragen. Keine Build-Steps, keine generierten Dateien — nur TypeScript-Inferenz über Modulgrenzen hinweg.

1. Installation und Grundstruktur

Ein neues tRPC-Projekt startet mit wenigen Paketen. Claude Code legt die Verzeichnisstruktur automatisch an:

# Installation für Next.js Projekt npm install @trpc/server @trpc/client @trpc/react-query @trpc/next npm install @tanstack/react-query zod # Projektstruktur die Claude Code generiert src/ server/ trpc.ts # tRPC Initialisierung + Context routers/ _app.ts # Root Router user.ts # User-Procedures post.ts # Post-Procedures utils/ trpc.ts # Client-seitiger tRPC-Hook pages/ api/trpc/[trpc].ts # Next.js API-Handler

INIT tRPC Initialisierung: server/trpc.ts

Die Basis jedes tRPC-Projekts: Context-Definition und initTRPC-Konfiguration.

import { initTRPC, TRPCError } from '@trpc/server'; import { type CreateNextContextOptions } from '@trpc/server/adapters/next'; import superjson from 'superjson'; // Context-Typ: wird in jeder Procedure verfügbar export async function createTRPCContext(opts: CreateNextContextOptions) { const { req, res } = opts; // Session aus Cookie oder Header lesen const session = await getServerSession(req, res); return { session, db, // Prisma Client oder anderer ORM req, }; } type Context = Awaited<ReturnType<typeof createTRPCContext>>; // tRPC Initialisierung mit SuperJSON für Date/BigInt-Serialisierung const t = initTRPC.context<Context>().create({ transformer: superjson, errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); // Exports: Router und Procedure-Bausteine export const router = t.router; export const publicProcedure = t.procedure; export const middleware = t.middleware;

2. Router und Procedures definieren

Procedures sind die Kernbausteine von tRPC. Claude Code unterscheidet dabei zwischen query (lesend, wie GET) und mutation (schreibend, wie POST/PUT/DELETE).

PROC User Router mit Zod-Validierung

import { z } from 'zod'; import { router, publicProcedure, protectedProcedure } from '../trpc'; import { TRPCError } from '@trpc/server'; export const userRouter = router({ // Query: Einzelnen User abrufen getById: publicProcedure .input(z.object({ id: z.string().uuid() })) .query(async ({ input, ctx }) => { const user = await ctx.db.user.findUnique({ where: { id: input.id }, select: { id: true, name: true, email: true }, }); if (!user) throw new TRPCError({ code: 'NOT_FOUND', message: 'User nicht gefunden', }); return user; }), // Query: Liste mit Paginierung list: publicProcedure .input(z.object({ limit: z.number().min(1).max(100).default(20), cursor: z.string().optional(), })) .query(async ({ input, ctx }) => { const { limit, cursor } = input; const users = await ctx.db.user.findMany({ take: limit + 1, cursor: cursor ? { id: cursor } : undefined, orderBy: { createdAt: 'desc' }, }); let nextCursor: typeof cursor = undefined; if (users.length > limit) { const next = users.pop(); nextCursor = next?.id; } return { users, nextCursor }; }), // Mutation: User erstellen create: publicProcedure .input(z.object({ name: z.string().min(2).max(100), email: z.string().email(), role: z.enum(['USER', 'ADMIN']).default('USER'), })) .mutation(async ({ input, ctx }) => { const exists = await ctx.db.user.findUnique({ where: { email: input.email }, }); if (exists) throw new TRPCError({ code: 'CONFLICT', message: 'Email bereits vergeben', }); return ctx.db.user.create({ data: input }); }), });

3. Middleware und geschützte Procedures

Authentifizierungs-Middleware ist eines der mächtigsten Features von tRPC. Claude Code generiert typensichere Middleware-Ketten, die den Context anreichern.

MIDDLEWARE Auth-Middleware + Protected Procedure

import { middleware, publicProcedure, TRPCError } from './trpc'; // Auth-Middleware: prüft Session, wirft Fehler wenn nicht eingeloggt const enforceUserIsAuthed = middleware(async ({ ctx, next }) => { if (!ctx.session?.user) { throw new TRPCError({ code: 'UNAUTHORIZED' }); } return next({ ctx: { // Session ist jetzt garantiert vorhanden — TypeScript weiß das! session: { ...ctx.session, user: ctx.session.user }, }, }); }); // Rate-Limiting Middleware const rateLimitMiddleware = middleware(async ({ ctx, next, path }) => { const key = `rate-limit:${ctx.session?.user?.id ?? ctx.req.ip}:${path}`; const requests = await redis.incr(key); if (requests === 1) await redis.expire(key, 60); if (requests > 60) throw new TRPCError({ code: 'TOO_MANY_REQUESTS', message: 'Zu viele Anfragen — bitte warte eine Minute', }); return next(); }); // Protected Procedure: kombiniert Auth + Rate-Limiting export const protectedProcedure = publicProcedure .use(enforceUserIsAuthed) .use(rateLimitMiddleware); // Admin Procedure: zusätzliche Rollenprüfung const enforceAdminRole = middleware(async ({ ctx, next }) => { if (ctx.session?.user?.role !== 'ADMIN') { throw new TRPCError({ code: 'FORBIDDEN' }); } return next(); }); export const adminProcedure = protectedProcedure.use(enforceAdminRole);

4. Root Router und Next.js API-Handler

ROUTER Root Router zusammenführen

// server/routers/_app.ts import { router } from ''../trpc'; import { userRouter } from './user'; import { postRouter } from './post'; import { analyticsRouter } from './analytics'; export const appRouter = router({ user: userRouter, post: postRouter, analytics: analyticsRouter, }); // Dieser Typ wird auf den Client exportiert — das ist tRPCs Magie export type AppRouter = typeof appRouter; // pages/api/trpc/[trpc].ts — Next.js Pages Router import { createNextApiHandler } from '@trpc/server/adapters/next'; import { appRouter } from '../../../server/routers/_app'; import { createTRPCContext } from '../../../server/trpc'; export default createNextApiHandler({ router: appRouter, createContext: createTRPCContext, onError({ error, path }) { if (error.code === 'INTERNAL_SERVER_ERROR') { console.error(`[tRPC Error] ${path}:`, error); } }, });

5. React Query Integration: useQuery und useMutation

Der Client-seitige Teil von tRPC baut auf TanStack Query auf. Claude Code generiert den Provider-Setup und zeigt, wie Queries und Mutations vollständig typsicher aufgerufen werden.

CLIENT tRPC Client Setup und Provider

// utils/trpc.ts — Client-seitiger tRPC-Hook import { createTRPCReact } from '@trpc/react-query'; import { type AppRouter } from '../server/routers/_app'; export const trpc = createTRPCReact<AppRouter>(); // _app.tsx — Provider-Setup import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { httpBatchLink, loggerLink } from '@trpc/client'; import superjson from 'superjson'; export default function App({ Component, pageProps }) { const [queryClient] = useState(() => new QueryClient({ defaultOptions: { queries: { staleTime: 5000 } }, })); const [trpcClient] = useState(() => trpc.createClient({ links: [ loggerLink({ enabled: () => process.env.NODE_ENV === 'development' }), httpBatchLink({ url: '/api/trpc', transformer: superjson, headers() { return { 'x-trpc-source': 'react' }; }, }), ], }) ); return ( <trpc.Provider client={trpcClient} queryClient={queryClient}> <QueryClientProvider client={queryClient}> <Component {...pageProps} /> </QueryClientProvider> </trpc.Provider> ); }

HOOKS useQuery und useMutation in Komponenten

// components/UserList.tsx import { trpc } from '../utils/trpc'; export function UserList() { // Vollständige Typsicherheit: input UND output sind typisiert const { data, isLoading, fetchNextPage } = trpc.user.list.useInfiniteQuery( { limit: 20 }, { getNextPageParam: (page) => page.nextCursor } ); const createUser = trpc.user.create.useMutation({ onSuccess(newUser) { // Cache automatisch invalidieren nach Mutation trpc.useUtils().user.list.invalidate(); console.log(`User erstellt: ${newUser.name}`); }, onError(error) { // Zod-Validierungsfehler direkt zugänglich const zodErrors = error.data?.zodError?.fieldErrors; console.error(zodErrors?.email?.[0] ?? error.message); }, }); if (isLoading) return <div>Lade...</div>; return ( <div> {data?.pages.flatMap(p => p.users).map(user => ( // user.id, user.name, user.email — alle typsicher! <UserCard key={user.id} user={user} /> ))} <button onClick={() => createUser.mutate({ name: 'Max Mustermann', email: 'max@example.com', })}> User hinzufügen </button> </div> ); }

6. Next.js App Router Integration

Mit dem Next.js App Router (Server Components) ändert sich die tRPC-Integration. Claude Code kennt beide Ansätze und wählt den richtigen für den jeweiligen Anwendungsfall.

APP ROUTER Server-seitiger tRPC-Aufruf (React Server Components)

// app/trpc/server.ts — Server-seitiger Client import { createTRPCProxyClient, httpBatchLink } from '@trpc/client'; import { cache } from 'react'; import superjson from 'superjson'; import { type AppRouter } from '../../server/routers/_app'; // cache() stellt sicher: pro Request nur ein Client const getServerClient = cache(() => createTRPCProxyClient<AppRouter>({ links: [httpBatchLink({ url: `${process.env.NEXT_PUBLIC_URL}/api/trpc`, transformer: superjson, })], }) ); // app/users/page.tsx — Server Component mit direktem tRPC-Aufruf import { getServerClient } from '../trpc/server'; export default async function UsersPage() { // Direkter Server-zu-Server-Aufruf — kein HTTP-Overhead! const { users } = await getServerClient().user.list.query({ limit: 10 }); return ( <main> <h1>Benutzer</h1> {users.map(u => <p key={u.id}>{u.name}</p>)} </main> ); } // Für Client-Komponenten im App Router: React Query Provider // app/providers.tsx 'use client'; import { TRPCReactProvider } from '../utils/trpc'; export function Providers({ children }) { return <TRPCReactProvider>{children}</TRPCReactProvider>; }
App Router Achtung: Server Components können tRPC direkt aufrufen (kein useQuery). Client Components (mit 'use client') brauchen weiterhin den React Query Provider und trpc.*.useQuery()-Hooks.

7. Zod-Validierung: Fortgeschrittene Muster

Claude Code nutzt Zods gesamten Funktionsumfang für komplexe Input-Validierungen:

// Komplexe Zod-Schemas die Claude Code generiert import { z } from 'zod'; // Diskriminierte Union für verschiedene Event-Typen const eventSchema = z.discriminatedUnion('type', [ z.object({ type: z.literal('USER_CREATED'), userId: z.string().uuid() }), z.object({ type: z.literal('POST_PUBLISHED'), postId: z.string().uuid(), publishedAt: z.date() }), ]); // Rekursives Schema für Kommentar-Threads type Comment = { id: string; text: string; replies: Comment[]; }; const commentSchema: z.ZodType<Comment> = z.lazy(() => z.object({ id: z.string(), text: z.string().min(1).max(2000), replies: z.array(commentSchema), }) ); // Custom Refinement: Geschäftslogik in der Validierung const dateRangeSchema = z.object({ from: z.date(), to: z.date(), }).refine( (data) => data.from < data.to, { message: 'Startdatum muss vor Enddatum liegen', path: ['from'] } );

8. tRPC vs REST vs GraphQL — Der Vergleich 2026

Wann ist tRPC die richtige Wahl? Claude Code hilft bei der Architekturentscheidung:

Kriterium tRPC REST + OpenAPI GraphQL
Typsicherheit ✓ Nativ, ohne Codegen ~ Via Codegen (openapi-ts) ~ Via Codegen (graphql-codegen)
Setup-Aufwand ✓ Minimal (~30min) ~ Mittel ✗ Hoch (Schema, Resolver, Codegen)
Externe API-Clients ✗ Nur TypeScript ✓ Jede Sprache ✓ Jede Sprache
Flexible Queries ~ Vordefinierte Felder ~ Query-Parameter ✓ Client wählt Felder
Performance ✓ HTTP Batching ✓ HTTP/2 native ~ N+1-Problem möglich
Ideal für Full-Stack TypeScript Monorepos Öffentliche APIs, Multi-Client Komplexe Datenmodelle, viele Clients
Claude Code Empfehlung 2026: Für neue Next.js- oder Remix-Projekte mit TypeScript auf beiden Seiten ist tRPC die erste Wahl. Sobald externe Clients (Mobile Apps anderer Teams, Drittanbieter) die API nutzen sollen, ist REST mit OpenAPI oder GraphQL sinnvoller.

Claude Code als tRPC-Assistent: Konkrete Prompts

Claude Code versteht tRPC auf Architekturebene. Diese Prompts liefern direkt verwendbare Ergebnisse:

Effektive Claude Code Prompts für tRPC

  • "Erstelle einen tRPC Router für Blog-Posts mit CRUD-Operationen, Zod-Validierung und Pagination — Prisma als ORM"
  • "Füge eine Auth-Middleware hinzu, die NextAuth-Sessions prüft und den Context typsicher erweitert"
  • "Migriere diese REST-API-Route zu einer tRPC Procedure mit identischem Verhalten"
  • "Erstelle einen optimistischen Update-Pattern mit useMutation und Cache-Invalidierung für die User-Liste"
  • "Zeige mir wie ich tRPC Procedures in Next.js App Router Server Actions einbinde"

Der entscheidende Vorteil: Claude Code sieht den gesamten Router-Typ und kann Client-Komponenten generieren, die perfekt zur Server-API passen — ohne einen manuellen Codegen-Schritt dazwischen.

API-Modul im Kurs

Im Claude Code Mastery Kurs: vollständiges tRPC-Modul mit Router-Design, Middleware, Authentifizierung, React Query Integration und Next.js App Router — für vollständige End-to-End-Typsicherheit.

14 Tage kostenlos testen →