Builds end-to-end type-safe tRPC APIs: routers, Zod procedures, dual Next.js App Router context factories, protected middleware, and subscriptions. Use when scaffolding tRPC in a TypeScript monorepo or migrating REST/GraphQL incrementally inside one repo. Not for OpenAPI/REST-only backends or GraphQL schema-first stacks. Do not share one context factory between fetch Request handlers and RSC callers.
Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Builds end-to-end type-safe tRPC APIs: routers, Zod procedures, dual Next.js App Router context factories, protected middleware, and subscriptions. Use when scaffolding tRPC in a TypeScript monorepo or migrating REST/GraphQL incrementally inside one repo. Not for OpenAPI/REST-only backends or GraphQL schema-first stacks. Do not share one context factory between fetch Request handlers and RSC callers.
tRPC lets you build fully type-safe APIs without writing a schema or code-generation step. TypeScript types flow from the server router directly to the client — every API call is autocompleted, validated at compile time, and refactoring-safe. Use this skill when building TypeScript monorepos, Next.js apps, or any project where the server and client share a codebase.
When to Use
Building a TypeScript full-stack app (Next.js App Router, Remix, Express + React) where client and server share a single repo
You want end-to-end type safety on API calls without REST/GraphQL schema overhead
Adding real-time features (subscriptions) to an existing tRPC setup
HARD RULE: Next.js App Router handlers receive a fetch Request, not a Node.js NextApiRequest. You must define separate context factories — one for the HTTP handler, one for direct server-side callers (Server Components, RSC, cron jobs).
// src/server/context.tsimport { typeFetchCreateContextFnOptions } from'@trpc/server/adapters/fetch';
import { auth } from'@/server/auth'; // Next-Auth v5 / your auth helperimport { db } from'./db';
/**
* Context for the HTTP handler (App Router Route Handler).
* opts.req is the fetch Request — auth is resolved server-side via auth().
*/exportasyncfunctioncreateTRPCContext(opts: FetchCreateContextFnOptions) {
const session = awaitauth();
return { session, db, headers: opts.req.headers };
}
/**
* Context for direct server-side callers (Server Components, RSC, cron jobs).
* No HTTP request is involved — call auth() directly from the server.
*/exportasyncfunctioncreateServerContext() {
const session = awaitauth();
return { session, db };
}
exporttypeContext = Awaited<ReturnType<typeof createTRPCContext>>;
Step 3: Build an Auth Middleware and Protected Procedure
// src/server/trpc.ts (continued)const enforceAuth = middleware(({ ctx, next }) => {
if (!ctx.session?.user) {
thrownewTRPCError({ code: 'UNAUTHORIZED' });
}
returnnext({
ctx: {
// Narrows type: session is non-null from here downstreamsession: { ...ctx.session, user: ctx.session.user },
},
});
});
exportconst protectedProcedure = t.procedure.use(enforceAuth);
Step 4: Create Domain Routers
Split routers by domain (posts, users, billing) and merge them in root.ts.
// src/server/root.tsimport { router } from'./trpc';
import { postRouter } from'./routers/post';
import { userRouter } from'./routers/user';
exportconst appRouter = router({
post: postRouter,
user: userRouter,
});
// Export the TYPE for the client — never import appRouter itself on the clientexporttypeAppRouter = typeof appRouter;
Step 6: Mount the API Handler (Next.js App Router)
HARD RULE: The App Router handler must use fetchRequestHandler from @trpc/server/adapters/fetch and the fetch-based context factory. Do NOT use @trpc/server/adapters/next — that adapter is for Pages Router only.
Auth session is null in protected procedures even when the user is logged in. Ensure createTRPCContext uses the correct server-side auth call (e.g. auth() from Next-Auth v5) and is not receiving a Pages Router req/res cast via as any in an App Router handler.
Server Component caller fails for auth-dependent queries. Use createServerContext() (the dedicated server-side factory) instead of passing an empty or synthetic object to createContext. Never use createContext({} as any).
"Type error: AppRouter is not assignable to AnyRouter". Import AppRouter as a type import (import type { AppRouter }) on the client, not the full module.
Mutations not reflecting in the UI after success. Call utils.<router>.<procedure>.invalidate() in onSuccess to trigger a refetch via React Query.
"Cannot find module '@trpc/server/adapters/next'" with App Router. Use @trpc/server/adapters/fetch and fetchRequestHandler for the App Router. The nextjs adapter is for Pages Router only.
Subscriptions not connecting. Subscriptions require splitLink — route subscriptions to wsLink and queries/mutations to httpBatchLink. Without splitLink, the client will attempt HTTP for subscription calls and fail silently.
Don't cast context with as any to silence type errors — the mismatch will surface as a runtime failure when auth or session lookups return undefined.
Don't share the tRPC client instance globally — create it per-provider to avoid stale closures and stale auth headers.
Don't put business logic in the route handler — keep it in the procedure or a service layer.
Always validate all input shapes with Zod, including pagination cursors and IDs, to prevent injection via malformed inputs.
Always enforce authorization in protectedProcedure — never rely on client-side checks alone.
Avoid exposing internal error details to clients — use TRPCError with a public-safe message and keep stack traces server-side only.
Verification
Type-check the server and client share the same router type:
npx tsc --noEmit
Expected: no errors. If you see "AppRouter is not assignable to AnyRouter," switch the client import to import type { AppRouter }.
Verify the API handler responds:
curl -X POST http://localhost:3000/api/trpc/post.list -H "content-type: application/json" -d '{"json":{"limit":5}}'
Expected: a JSON response with a result object containing posts and nextCursor.
curl -X POST http://localhost:3000/api/trpc/post.create -H "content-type: application/json" -d '{"json":{"title":"test","body":"test"}}'
Expected: an error response with code: 'UNAUTHORIZED'.
Verify the client type-safety in the editor: In a client component, type trpc.post. and confirm autocomplete shows list, byId, create, delete. If autocomplete is empty, the AppRouter type import is missing or wrong.
Verify server-side caller works in a Server Component: Navigate to the /posts route and confirm posts render without a UNAUTHORIZED or context error.
Related Skills
typescript-expert — Deep TypeScript patterns used inside tRPC routers and generic utilities
react-patterns — React hooks patterns that pair with trpc.*.useQuery and useMutation
test-driven-development — Write procedure unit tests using createCallerFactory without an HTTP server
security-auditor — Review tRPC middleware chains for auth bypass and input validation gaps