/
githubmirror
/
trpc
Обзор
Документация
Войти
/
githubmirror
/
trpc
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
_artifacts/domain_map.yaml
1 769 строк
67 KB
Nick Lucas
feat: Tanstack Intent Skills (#7252)
21 мар 2026, 02:18
Не верифицирован
21 мар 2026, 02:18
e39a654
Код
Авторство
О чём код?
# domain_map.yaml # Generated by skill-domain-discovery # Library: tRPC # Version: 11.13.4 # Date: 2026-03-15 # Status: reviewed library: name: '@trpc/server' version: '11.14.0' repository: 'https://github.com/trpc/trpc' description: > End-to-end typesafe APIs for TypeScript. tRPC lets you define server procedures (query, mutation, subscription) and call them from clients with full static type safety and autocompletion, without schemas or code generation. primary_framework: 'framework-agnostic' domains: - name: 'Defining the API' slug: 'defining-api' description: > Server-side setup: initializing tRPC, defining routers, procedures, context, middleware, validators, error handling, and metadata. - name: 'Consuming the API' slug: 'consuming-api' description: > Client-side setup: creating tRPC clients, configuring links, headers, transformers, and calling procedures from vanilla JS or React. - name: 'Hosting the API' slug: 'hosting-api' description: > Mounting tRPC on a server or serverless runtime via adapters (standalone, Express, Fastify, Next.js, AWS Lambda, Fetch/edge). - name: 'Real-time' slug: 'realtime' description: > Subscriptions via Server-Sent Events or WebSockets, including tracked events, reconnection, and streaming patterns. - name: 'Framework integration' slug: 'framework-integration' description: > Using tRPC with specific frameworks (Next.js, React via TanStack Query) including SSR, server components, and hydration. - name: 'Interop and publishing' slug: 'interop' description: > Generating OpenAPI specs, publishing typed REST clients, and service-oriented architecture patterns. skills: # ── Defining the API ────────────────────────────────────────────── - name: 'Server setup' slug: 'server-setup' domain: 'defining-api' description: > Initialize tRPC, define routers with query/mutation/subscription procedures, configure context, and export AppRouter type. type: core packages: - '@trpc/server' covers: - 'initTRPC.create()' - 't.router()' - 't.procedure / publicProcedure' - '.query() / .mutation() / .subscription()' - 'createContext()' - 'mergeRouters()' - 'lazy() router loading' - 'export type AppRouter' tasks: - 'Initialize a new tRPC backend from scratch' - 'Add a new query or mutation procedure' - 'Define and export the AppRouter type for clients' - 'Set up context with request-scoped data' - 'Split context into inner/outer for testability' - 'Lazy-load routers for serverless cold start optimization' - 'Merge multiple routers into one' failure_modes: - mistake: 'Calling initTRPC.create() more than once' mechanism: > Multiple tRPC instances cause type mismatches and runtime errors when routers from different instances are merged. wrong_pattern: | // file: userRouter.ts const t = initTRPC.create(); // file: postRouter.ts const t2 = initTRPC.create(); // second instance! correct_pattern: | // file: trpc.ts (single file) const t = initTRPC.context<Context>().create(); export const router = t.router; export const publicProcedure = t.procedure; source: 'www/docs/server/routers.md' priority: CRITICAL status: active - mistake: 'Using reserved words as procedure names' mechanism: > Router creation throws if procedure names are "then", "call", or "apply" — these conflict with JavaScript Proxy internals. wrong_pattern: | const appRouter = router({ then: publicProcedure.query(() => 'hello'), }); correct_pattern: | const appRouter = router({ next: publicProcedure.query(() => 'hello'), }); source: 'packages/server/src/unstable-core-do-not-import/router.ts:210-221' priority: HIGH status: active - mistake: 'Importing AppRouter as a value import' mechanism: > A non-type import pulls the entire server bundle into the client. Must use `import type` so it is stripped at build time. wrong_pattern: | import { AppRouter } from '../server/router'; correct_pattern: | import type { AppRouter } from '../server/router'; source: 'maintainer interview' priority: CRITICAL status: active - mistake: 'Creating context without inner/outer split' mechanism: > Without an inner context factory, server-side callers (tests, internal logic) must construct HTTP request objects to get context. wrong_pattern: | export function createContext({ req }: CreateExpressContextOptions) { return { db: prisma, user: getUserFromReq(req) }; } correct_pattern: | export function createContextInner(opts: { user?: User }) { return { db: prisma, user: opts.user ?? null }; } export function createContext({ req }: CreateExpressContextOptions) { return createContextInner({ user: getUserFromReq(req) }); } source: 'examples/next-prisma-starter/src/server/context.ts' priority: MEDIUM status: active - mistake: 'Merging routers with different transformers' mechanism: > t.mergeRouters() throws at runtime if the routers were created with different transformer or errorFormatter configurations. wrong_pattern: | const t1 = initTRPC.create({ transformer: superjson }); const t2 = initTRPC.create(); // default transformer t1.mergeRouters(router1, router2); correct_pattern: | // Use a single initTRPC instance for all routers const t = initTRPC.create({ transformer: superjson }); source: 'packages/server/src/unstable-core-do-not-import/router.ts:517-545' priority: HIGH status: active - mistake: 'Hallucinating v10 API patterns' mechanism: > AI agents trained on older data generate v10 code: using createTRPCProxyClient (renamed to createTRPCClient), putting transformer on the client constructor (moved to links), or using Observable subscriptions (deprecated for async generators). Refer to the v10→v11 migration guide for all changed APIs. wrong_pattern: | import { createTRPCProxyClient } from '@trpc/client'; correct_pattern: | import { createTRPCClient } from '@trpc/client'; source: 'www/docs/migration/migrate-from-v10-to-v11.mdx' priority: CRITICAL status: active skills: ['server-setup', 'client-setup', 'links', 'subscriptions'] - mistake: 'Using type assertions to work around AppRouter import errors' mechanism: > When AppRouter type import fails (monorepo config, path issues), agents cast to `as any` or inline a client-only representation of the type. This destroys type safety. Fix the import path or monorepo configuration instead. wrong_pattern: | const client = createTRPCClient<any>({ links: [...] }); // or type AppRouter = { user: { get: ... } }; // manually recreated correct_pattern: | // Fix the actual import — use monorepo tools (Nx, turborepo) // or publish the type from a shared package import type { AppRouter } from '@myorg/api-types'; source: 'maintainer interview' priority: CRITICAL status: active skills: ['server-setup', 'client-setup'] - mistake: 'Treating tRPC as a REST API' mechanism: > AI agents may try to call tRPC endpoints with fetch() using REST conventions (GET /api/users/123). tRPC uses JSON-RPC over HTTP — procedures are called by name with JSON input, not by REST resource paths. wrong_pattern: | fetch('/api/trpc/users/123', { method: 'GET' }) correct_pattern: | // Use the tRPC client const user = await trpc.users.getById.query({ id: '123' }); // Or raw: GET /api/trpc/users.getById?input={"id":"123"} source: 'maintainer interview' priority: CRITICAL status: active skills: ['server-setup', 'client-setup'] - mistake: 'Importing appRouter value (not type) into client' mechanism: > Importing the appRouter value (not just the type) bundles the entire server into the client. The router must only be imported as a type. Even if it works at build time, it ships server code to the browser. wrong_pattern: | import { appRouter } from '../server/router'; type AppRouter = typeof appRouter; // value import! correct_pattern: | // In server: export type AppRouter = typeof appRouter; // In client: import type { AppRouter } from '../server/router'; source: 'maintainer interview' priority: CRITICAL status: active compositions: - library: 'Zod' skill: 'validators' - library: 'SuperJSON' skill: 'superjson' - name: 'Middlewares' slug: 'middlewares' domain: 'defining-api' description: > Create and compose middleware for auth checks, logging, context extension, and reusable base procedures. type: core packages: - '@trpc/server' covers: - '.use() middleware chaining' - 'opts.next() with context extension' - '.concat() for reusable middleware' - '.unstable_pipe() for middleware composition' - 'Base procedure pattern (publicProcedure, authedProcedure)' - 'Advanced: OTEL tracing, logging inputs/outputs' - 'Advanced: getRawInput() for manual input processing' tasks: - 'Create an auth middleware that narrows context type' - 'Build a reusable logging/timing middleware' - 'Compose multiple middlewares into a base procedure' - 'Use .concat() to create portable middleware plugins' - 'Add OTEL tracing spans around procedure execution' - 'Log procedure inputs and outputs for debugging' failure_modes: - mistake: 'Forgetting to call and return opts.next()' mechanism: > Middleware must call opts.next() and return its result. Forgetting this silently drops the request with an INTERNAL_SERVER_ERROR because no middlewareMarker is returned. wrong_pattern: | const logMiddleware = t.middleware(async (opts) => { console.log('request started'); // forgot to call opts.next() }); correct_pattern: | const logMiddleware = t.middleware(async (opts) => { console.log('request started'); const result = await opts.next(); console.log('request ended'); return result; }); source: 'packages/server/src/unstable-core-do-not-import/procedureBuilder.ts:684-689' priority: CRITICAL status: active - mistake: 'Extending context with wrong type in opts.next()' mechanism: > Context extension in opts.next({ ctx: ... }) must be an object. Passing non-object values or overwriting existing required keys breaks downstream procedures. wrong_pattern: | const middleware = t.middleware(async (opts) => { return opts.next({ ctx: 'not-an-object' }); }); correct_pattern: | const middleware = t.middleware(async (opts) => { return opts.next({ ctx: { user: await getUser() } }); }); source: 'www/docs/server/middlewares.md' priority: HIGH status: active - mistake: 'Using experimental_standaloneMiddleware (deprecated)' mechanism: > experimental_standaloneMiddleware is deprecated. Use .concat() instead for creating reusable middleware independent of the tRPC instance context. wrong_pattern: | const mw = experimental_standaloneMiddleware().create(async (opts) => { return opts.next(); }); correct_pattern: | const mw = t.procedure.use(async (opts) => { return opts.next(); }); // Or use .concat() for portable middleware source: 'packages/server/src/unstable-core-do-not-import/middleware.ts:166' priority: MEDIUM status: active compositions: [] - name: 'Validators' slug: 'validators' domain: 'defining-api' description: > Configure input and output validation with Zod or other Standard Schema validators. Handle non-serializable types via transformers. type: core packages: - '@trpc/server' covers: - '.input() with Zod, Yup, Superstruct, ArkType, Valibot, Effect' - '.output() for response validation' - 'Input chaining / merging' - 'Standard Schema protocol' - 'Custom validator functions' tasks: - 'Add Zod input validation to a procedure' - 'Add output validation for untrusted data sources' - 'Chain multiple .input() calls for composable schemas' - 'Use a custom validator function' failure_modes: - mistake: 'Chaining non-object inputs' mechanism: > Multiple .input() calls merge object types. Non-object schemas (string, number, array) cannot be chained and will produce type errors. wrong_pattern: | publicProcedure .input(z.string()) .input(z.number()) // cannot merge string + number correct_pattern: | publicProcedure .input(z.object({ name: z.string() })) .input(z.object({ age: z.number() })) // merges to { name: string, age: number } source: 'www/docs/server/validators.md' priority: MEDIUM status: active - mistake: 'Output validation failure returns 500' mechanism: > If .output() validation fails, tRPC returns INTERNAL_SERVER_ERROR (500), not BAD_REQUEST. This can be confusing during development. wrong_pattern: | publicProcedure .output(z.object({ id: z.string() })) .query(() => ({ id: 123 })) // number, not string — 500 error correct_pattern: | publicProcedure .output(z.object({ id: z.string() })) .query(() => ({ id: '123' })) // string matches schema source: 'www/docs/server/validators.md' priority: MEDIUM status: active - mistake: 'Using cursor: z.optional() without nullable for infinite queries' mechanism: > React Query internally passes cursor: undefined during invalidation refetch. If the Zod schema uses .optional() without .nullable(), this fails validation with a 400 error. wrong_pattern: | .input(z.object({ cursor: z.string().optional() })) correct_pattern: | .input(z.object({ cursor: z.string().nullish() })) source: 'https://github.com/trpc/trpc/issues/6862' priority: HIGH status: active compositions: - library: 'Zod' - name: 'Error handling' slug: 'error-handling' domain: 'defining-api' description: > Throw and format errors using TRPCError, configure errorFormatter for client-side consumption, and handle errors globally via onError. type: core packages: - '@trpc/server' covers: - 'TRPCError class and error codes' - 'errorFormatter configuration' - 'onError callback in adapters' - 'getHTTPStatusCodeFromError()' - 'Stack trace behavior (isDev)' tasks: - 'Throw typed errors from procedures' - 'Format Zod validation errors for client display' - 'Set up global error logging via onError' - 'Map tRPC errors to HTTP status codes' failure_modes: - mistake: 'Throwing plain Error instead of TRPCError' mechanism: > Plain Error objects are caught and wrapped as INTERNAL_SERVER_ERROR. Use TRPCError with a specific code for proper HTTP status mapping. wrong_pattern: | throw new Error('Not found'); // → 500 INTERNAL_SERVER_ERROR correct_pattern: | throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found', }); // → 404 NOT_FOUND source: 'www/docs/server/error-handling.md' priority: HIGH status: active - mistake: 'Expecting stack traces in production' mechanism: > Stack traces are included only when isDev is true (default: NODE_ENV !== "production"). Set isDev explicitly for deterministic behavior across runtimes. source: 'www/docs/server/error-handling.md' priority: MEDIUM status: active - mistake: 'Not handling Zod errors in errorFormatter' mechanism: > Zod validation errors are wrapped as BAD_REQUEST TRPCErrors. Without a custom errorFormatter, the client receives a generic message without field-level details. wrong_pattern: | // No errorFormatter — client gets: "Input validation failed" initTRPC.create(); correct_pattern: | initTRPC.create({ errorFormatter({ shape, error }) { return { ...shape, data: { ...shape.data, zodError: error.cause instanceof ZodError ? error.cause.flatten() : null, }, }; }, }); source: 'www/docs/server/error-formatting.md' priority: HIGH status: active compositions: [] - name: 'Server-side calls' slug: 'server-side-calls' domain: 'defining-api' description: > Call procedures directly from server code using createCallerFactory for integration testing and internal logic. type: core packages: - '@trpc/server' covers: - 't.createCallerFactory()' - 'router.createCaller(context)' - 'Testing patterns' tasks: - 'Write integration tests against tRPC procedures' - 'Call a procedure from server-side logic without HTTP' failure_modes: - mistake: 'Using createCaller inside another procedure' mechanism: > Calling createCaller from within a procedure re-creates context, re-runs all middleware, and re-validates input. Extract shared logic into a plain function instead. wrong_pattern: | const caller = t.createCallerFactory(appRouter)({}); publicProcedure.query(async () => { return caller.otherProcedure(); // wasteful }); correct_pattern: | async function getSharedData(ctx: Context) { return ctx.db.query(...); } publicProcedure.query(async ({ ctx }) => { return getSharedData(ctx); }); source: 'www/docs/server/server-side-calls.md' priority: HIGH status: active - mistake: 'Not providing context to createCaller' mechanism: > createCaller requires a context object. Passing undefined or empty object when procedures expect auth context causes runtime errors in middleware. wrong_pattern: | const caller = appRouter.createCaller({}); await caller.protectedRoute(); // middleware throws UNAUTHORIZED correct_pattern: | const caller = appRouter.createCaller( await createContextInner({ user: testUser }) ); await caller.protectedRoute(); source: 'www/docs/server/server-side-calls.md' priority: MEDIUM status: active compositions: [] - name: 'Caching' slug: 'caching' domain: 'defining-api' description: > Set HTTP cache headers on query responses via responseMeta for CDN and browser caching. type: core packages: - '@trpc/server' covers: - 'responseMeta callback' - 'Cache-Control / s-maxage / stale-while-revalidate' - 'Interaction with batching' tasks: - 'Add CDN caching headers to public query responses' - 'Prevent caching on authenticated requests' failure_modes: - mistake: 'Caching authenticated responses' mechanism: > With batching enabled by default, a cached response containing personal data could be served to other users. Use splitLink to separate public/private requests, or check for auth headers. wrong_pattern: | responseMeta() { return { headers: { 'cache-control': 's-maxage=60' } }; } correct_pattern: | responseMeta({ ctx, errors, type }) { if (ctx?.user || errors.length > 0 || type !== 'query') return {}; return { headers: new Headers([ ['cache-control', 's-maxage=1, stale-while-revalidate=86400'], ]), }; } source: 'www/docs/server/caching.md' priority: CRITICAL status: active - mistake: 'Caching with Next.js App Router Cache-Control overrides' mechanism: > Next.js App Router overrides Cache-Control headers set by tRPC. The documented caching approach using responseMeta does not work as expected in App Router. source: 'https://github.com/trpc/trpc/issues/5625' priority: HIGH status: active compositions: [] - name: 'Non-JSON content types' slug: 'non-json-content-types' domain: 'defining-api' description: > Handle FormData, file uploads, Blob, Uint8Array, and ReadableStream inputs in tRPC procedures. type: core packages: - '@trpc/server' - '@trpc/client' covers: - 'FormData input parsing' - 'octetInputParser for binary data' - 'File / Blob / Uint8Array handling' - 'Content-Type detection' - 'splitLink for non-JSON routing' tasks: - 'Accept file uploads via FormData in a mutation' - 'Handle binary data with octetInputParser' - 'Route non-JSON requests with splitLink' failure_modes: - mistake: 'Using httpBatchLink for FormData requests' mechanism: > FormData and binary inputs are not batchable. Use splitLink with isNonJsonSerializable() to route them through httpLink. wrong_pattern: | // All requests go through httpBatchLink links: [httpBatchLink({ url })] correct_pattern: | import { isNonJsonSerializable } from '@trpc/client'; links: [ splitLink({ condition: (op) => isNonJsonSerializable(op.input), true: httpLink({ url }), false: httpBatchLink({ url }), }), ] source: 'www/docs/server/non-json-content-types.md' priority: HIGH status: active - mistake: 'Global body parser intercepting FormData before tRPC' mechanism: > Express global express.json() or Next.js default body parsing consumes the request body before tRPC can read it. Disable body parsing for tRPC routes. wrong_pattern: | // Express: global json parser app.use(express.json()); app.use('/trpc', createExpressMiddleware({ router })); correct_pattern: | // Only parse JSON for non-tRPC routes app.use('/api', express.json()); app.use('/trpc', createExpressMiddleware({ router })); source: 'www/docs/server/non-json-content-types.md' priority: HIGH status: active - mistake: 'FormData only works with mutations' mechanism: > FormData and binary inputs are only supported for mutations (POST requests). Using them with queries throws an error. source: 'packages/client/src/links/httpLink.ts:42-44' priority: HIGH status: active compositions: [] # ── Consuming the API ───────────────────────────────────────────── - name: 'Client setup' slug: 'client-setup' domain: 'consuming-api' description: > Create a vanilla tRPC client with links, transformers, and headers for calling procedures from any TypeScript environment. type: core packages: - '@trpc/client' - '@trpc/server' covers: - 'createTRPCClient<AppRouter>()' - 'Link chain configuration' - 'Headers (static and dynamic)' - 'Transformer setup on links' - 'AbortController / signal support' - 'inferRouterInputs / inferRouterOutputs' tasks: - 'Create a vanilla tRPC client for a backend or CLI' - 'Configure authentication headers' - 'Set up a transformer on client links' - 'Infer procedure input/output types on the client' failure_modes: - mistake: 'Missing AppRouter type parameter on createTRPCClient' mechanism: > Without the type parameter, all procedure calls return `any` and type safety is completely lost. wrong_pattern: | const client = createTRPCClient({ links: [...] }); correct_pattern: | const client = createTRPCClient<AppRouter>({ links: [...] }); source: 'www/docs/client/vanilla/setup.mdx' priority: CRITICAL status: active - mistake: 'Transformer on server but not on client links' mechanism: > If the server uses a transformer (e.g. superjson), every terminating link on the client must also specify that transformer. Mismatch causes "Unable to transform response" errors. wrong_pattern: | // Server: initTRPC.create({ transformer: superjson }) // Client: httpBatchLink({ url: '/api/trpc' }) // no transformer! correct_pattern: | httpBatchLink({ url: '/api/trpc', transformer: superjson }) source: 'https://github.com/trpc/trpc/issues/7083' priority: CRITICAL status: active - mistake: 'Passing transformer to createTRPCClient instead of links' mechanism: > In v11, transformer config was moved from the client constructor to individual links. Passing it to createTRPCClient throws a TypeError. wrong_pattern: | createTRPCClient<AppRouter>({ transformer: superjson, links: [httpBatchLink({ url })], }); correct_pattern: | createTRPCClient<AppRouter>({ links: [httpBatchLink({ url, transformer: superjson })], }); source: 'packages/client/src/internals/TRPCUntypedClient.ts:44' priority: CRITICAL status: active - mistake: 'HTML error page instead of JSON response' mechanism: > Client error like "couldn't parse JSON, invalid character '<'" means the tRPC endpoint returned HTML (404/503 page) instead of JSON. This indicates the API URL is wrong or the infrastructure routing is misconfigured — not a tRPC bug. source: 'maintainer interview' priority: HIGH status: active - mistake: 'Confusion about which client factory to use' mechanism: > tRPC has several client creators across packages. Most users need only one: createTRPCClient from @trpc/client for vanilla, createTRPCContext from @trpc/tanstack-react-query for React, or createTRPCNext from @trpc/next for Pages Router. source: 'maintainer interview' priority: HIGH status: active - mistake: 'Worrying about @trpc/server as a client dependency' mechanism: > It is normal and expected for client packages to depend on @trpc/server — types are imported from there. These are type-only imports stripped at build time; no server code is bundled into the client. source: 'maintainer interview' priority: MEDIUM status: active compositions: - library: 'SuperJSON' skill: 'superjson' - name: 'Links' slug: 'links' domain: 'consuming-api' description: > Configure the client link chain to control batching, streaming, splitting, logging, and retry behavior. type: core packages: - '@trpc/client' covers: - 'httpLink' - 'httpBatchLink' - 'httpBatchStreamLink' - 'splitLink' - 'loggerLink' - 'wsLink / createWSClient' - 'httpSubscriptionLink' - 'unstable_localLink' - 'retryLink (internal)' - 'Custom link authoring' tasks: - 'Choose the right terminating link for a use case' - 'Enable request batching with httpBatchLink' - 'Enable streaming responses with httpBatchStreamLink' - 'Route subscriptions to a separate link with splitLink' - 'Add request logging for development' - 'Build a custom link for service-oriented routing' subsystems: - name: 'httpLink' config_surface: 'url, fetch, headers, transformer, methodOverride' - name: 'httpBatchLink' config_surface: 'url, maxURLLength, maxItems, headers, transformer' - name: 'httpBatchStreamLink' config_surface: 'url, streamHeader, headers, transformer' - name: 'wsLink' config_surface: 'createWSClient url, connectionParams, lazy, keepAlive, retryDelayMs' - name: 'httpSubscriptionLink' config_surface: 'url, connectionParams, EventSource ponyfill, eventSourceOptions' reference_candidates: - topic: 'Link options reference' reason: '>10 distinct link types each with unique configuration surfaces' failure_modes: - mistake: 'No terminating link in the chain' mechanism: > The link chain must end with a terminating link (httpLink, httpBatchLink, etc.). Missing one throws "No more links to execute - did you forget to add an ending link?" source: 'packages/client/src/links/internals/createChain.ts:21-24' priority: CRITICAL status: active - mistake: 'Sending subscriptions through httpLink or httpBatchLink' mechanism: > httpLink and httpBatchLink throw if they receive a subscription operation. Subscriptions must use httpSubscriptionLink or wsLink, typically via splitLink routing. wrong_pattern: | links: [httpBatchLink({ url })] // Then: client.onMessage.subscribe() → throws correct_pattern: | links: [ splitLink({ condition: (op) => op.type === 'subscription', true: httpSubscriptionLink({ url }), false: httpBatchLink({ url }), }), ] source: 'packages/client/src/links/httpLink.ts:85-88' priority: CRITICAL status: active - mistake: 'Batch headers callback using wrong parameter name' mechanism: > httpBatchLink headers callback receives { opList } (array), not { op } (single). Using the wrong name silently gets undefined. wrong_pattern: | httpBatchLink({ url, headers({ op }) { // undefined for batch link! return { authorization: op.context.token }; }, }) correct_pattern: | httpBatchLink({ url, headers({ opList }) { return { authorization: opList[0]?.context.token }; }, }) source: 'packages/client/src/links/httpBatchLink.ts:70' priority: HIGH status: active - mistake: 'httpBatchStreamLink data loss on stream completion' mechanism: > There is a known race condition where buffered chunks can be lost on normal stream completion. Long streaming responses (e.g., LLM output) may be truncated. source: 'https://github.com/trpc/trpc/issues/7209' priority: HIGH status: active - mistake: 'Default batch limits are Infinity' mechanism: > httpBatchLink maxURLLength and maxItems both default to Infinity. All concurrent operations batch into a single request, which can cause issues with URL length limits on some servers/CDNs. source: 'packages/client/src/links/httpBatchLink.ts:25-26' priority: MEDIUM status: active compositions: [] - name: 'TanStack React Query setup' slug: 'react-query-setup' domain: 'consuming-api' description: > Set up @trpc/tanstack-react-query with providers, use queryOptions and mutationOptions factories with TanStack React Query hooks. type: framework packages: - '@trpc/tanstack-react-query' - '@trpc/client' - '@trpc/server' covers: - 'createTRPCContext()' - 'TRPCProvider' - 'useTRPC() hook' - 'trpc.procedure.queryOptions()' - 'trpc.procedure.mutationOptions()' - 'trpc.procedure.subscriptionOptions()' - 'trpc.procedure.infiniteQueryOptions()' - 'trpc.procedure.queryKey() / queryFilter()' - 'inferInput / inferOutput type helpers' tasks: - 'Set up TRPCProvider with QueryClient and tRPC client' - 'Fetch data with useQuery + queryOptions' - 'Mutate data with useMutation + mutationOptions' - 'Invalidate queries after mutations' - 'Use infinite queries with cursor-based pagination' - 'Subscribe to real-time data with subscriptionOptions' failure_modes: - mistake: 'Using useQuery without queryOptions factory' mechanism: > The @trpc/tanstack-react-query package is designed around options factories. Calling useQuery directly without trpc.procedure.queryOptions() loses type safety. wrong_pattern: | useQuery({ queryKey: ['user', id], queryFn: () => fetch(...) }) correct_pattern: | const trpc = useTRPC(); useQuery(trpc.user.byId.queryOptions({ id })); source: 'www/docs/client/tanstack-react-query/usage.mdx' priority: HIGH status: active - mistake: 'Missing TRPCProvider wrapper' mechanism: > useTRPC() throws "can only be used inside of a <TRPCProvider>" if the component tree is not wrapped. source: 'packages/tanstack-react-query/src/internals/Context.tsx' priority: HIGH status: active - mistake: 'Invalidating queries with wrong API' mechanism: > The new package uses queryClient.invalidateQueries() with trpc.procedure.queryFilter(), not the classic utils.invalidate() pattern. wrong_pattern: | // Classic pattern doesn't work with new package utils.post.invalidate(); correct_pattern: | const trpc = useTRPC(); const queryClient = useQueryClient(); queryClient.invalidateQueries(trpc.post.queryFilter()); source: 'www/docs/client/tanstack-react-query/migrating.mdx' priority: HIGH status: active compositions: - library: '@tanstack/react-query' - name: 'Classic React Query migration' slug: 'react-query-classic-migration' domain: 'consuming-api' description: > Migrate from @trpc/react-query (classic) to @trpc/tanstack-react-query using the upgrade CLI and manual fixes. type: lifecycle packages: - '@trpc/tanstack-react-query' - '@trpc/react-query' covers: - 'npx @trpc/upgrade CLI' - 'Hook → options factory migration patterns' - 'utils.invalidate → queryClient.invalidateQueries migration' - 'Provider migration' tasks: - 'Run the upgrade CLI to automate migration' - 'Manually fix remaining type errors after upgrade' - 'Migrate invalidation patterns' - 'Verify TypeScript passes after migration' failure_modes: - mistake: 'Assuming the codemod handles everything' mechanism: > npx @trpc/upgrade is a work-in-progress codemod. It handles common patterns but may miss complex cases. Always typecheck after running it. source: 'www/docs/client/tanstack-react-query/migrating.mdx' priority: MEDIUM status: active - mistake: 'Mixing classic and new hooks in same component' mechanism: > While the classic and new packages can coexist in the same app, mixing their hooks in the same component creates confusing dual-provider requirements. source: 'www/docs/client/tanstack-react-query/migrating.mdx' priority: MEDIUM status: active compositions: [] # ── Hosting the API ─────────────────────────────────────────────── - name: 'Adapter: Standalone' slug: 'adapter-standalone' domain: 'hosting-api' description: > Mount tRPC on Node.js built-in HTTP server using the standalone adapter. Simplest hosting option, ideal for local dev. type: core packages: - '@trpc/server' covers: - 'createHTTPServer()' - 'createHTTPHandler()' - 'createHTTP2Handler()' - 'basePath configuration' - 'CORS via cors package middleware' tasks: - 'Create a standalone tRPC HTTP server' - 'Add CORS support with the cors package' - 'Set a custom base path for the tRPC endpoint' - 'Use HTTP/2 with TLS' failure_modes: - mistake: 'No CORS configuration' mechanism: > The standalone adapter has no CORS handling by default. Cross-origin requests from browsers will fail silently. Use the cors package as middleware. wrong_pattern: | createHTTPServer({ router: appRouter }).listen(3000); correct_pattern: | import cors from 'cors'; createHTTPServer({ router: appRouter, middleware: cors(), }).listen(3000); source: 'www/docs/server/adapters/standalone.md' priority: HIGH status: active compositions: [] - name: 'Adapter: Express' slug: 'adapter-express' domain: 'hosting-api' description: > Mount tRPC as Express middleware using createExpressMiddleware. type: core packages: - '@trpc/server' covers: - 'createExpressMiddleware()' - 'CreateExpressContextOptions (req, res)' tasks: - 'Add tRPC to an existing Express app' - 'Access Express req/res in context' failure_modes: - mistake: 'Global express.json() consuming tRPC request body' mechanism: > If express.json() is applied globally before the tRPC middleware, it consumes the request body. tRPC then receives an already-parsed body, which breaks for non-JSON content types like FormData. source: 'www/docs/server/non-json-content-types.md' priority: HIGH status: active compositions: - library: 'Express' - name: 'Adapter: Fastify' slug: 'adapter-fastify' domain: 'hosting-api' description: > Mount tRPC as a Fastify plugin with optional WebSocket support. type: core packages: - '@trpc/server' covers: - 'fastifyTRPCPlugin' - 'FastifyTRPCPluginOptions' - 'useWSS flag for WebSocket support' - 'prefix configuration' tasks: - 'Add tRPC to an existing Fastify app' - 'Enable WebSocket subscriptions in Fastify' failure_modes: - mistake: 'Registering @fastify/websocket after tRPC plugin' mechanism: > The WebSocket plugin must be registered before the tRPC plugin. Reverse order causes WebSocket routes to not be recognized. wrong_pattern: | server.register(fastifyTRPCPlugin, { useWSS: true, ... }); server.register(fastifyWebsocket); // too late! correct_pattern: | server.register(fastifyWebsocket); server.register(fastifyTRPCPlugin, { useWSS: true, ... }); source: 'www/docs/server/adapters/fastify.md' priority: HIGH status: active - mistake: 'Missing maxParamLength for batch requests' mechanism: > Fastify defaults to maxParamLength: 100. Batch requests with multiple procedures exceed this limit. Set maxParamLength: 5000. wrong_pattern: | const server = Fastify(); correct_pattern: | const server = Fastify({ maxParamLength: 5000 }); source: 'www/docs/server/adapters/fastify.md' priority: HIGH status: active - mistake: 'Using Fastify v4 with tRPC v11' mechanism: > tRPC v11 requires Fastify v5+. Fastify v4 may return empty responses due to incompatible response handling. source: 'www/docs/server/adapters/fastify.md' priority: CRITICAL status: active compositions: - library: 'Fastify' - name: 'Adapter: AWS Lambda' slug: 'adapter-aws-lambda' domain: 'hosting-api' description: > Deploy tRPC on AWS Lambda with API Gateway (v1/v2), Function URLs, and optional response streaming. type: core packages: - '@trpc/server' covers: - 'awsLambdaRequestHandler()' - 'awsLambdaStreamingRequestHandler()' - 'API Gateway v1 (REST) vs v2 (HTTP) payload formats' - 'Lambda Function URL support' - 'Response streaming with streamifyResponse()' tasks: - 'Deploy a tRPC API on AWS Lambda with API Gateway' - 'Enable response streaming for async generator procedures' - 'Configure batch requests with API Gateway' failure_modes: - mistake: 'Using httpBatchLink with per-procedure API Gateway resources' mechanism: > httpBatchLink sends multiple procedure names in the URL path. If API Gateway routes are per-procedure, batched requests 404. Use a single catch-all resource or httpLink. source: 'www/docs/server/adapters/aws-lambda.md' priority: HIGH status: active - mistake: 'Forgetting streamifyResponse wrapper for streaming' mechanism: > awsLambdaStreamingRequestHandler requires wrapping with awslambda.streamifyResponse() to enable Lambda response streaming. source: 'www/docs/server/adapters/aws-lambda.md' priority: HIGH status: active compositions: [] - name: 'Adapter: Fetch / Edge' slug: 'adapter-fetch' domain: 'hosting-api' description: > Deploy tRPC on WinterCG-compliant edge runtimes using the fetch adapter (Cloudflare Workers, Deno Deploy, Vercel Edge). type: core packages: - '@trpc/server' covers: - 'fetchRequestHandler()' - 'FetchCreateContextFnOptions (req, resHeaders)' - 'Cloudflare Workers, Deno, Vercel Edge, Remix, SolidStart, Astro' tasks: - 'Deploy tRPC on Cloudflare Workers' - 'Deploy tRPC on Vercel Edge Runtime' - 'Set up tRPC as a route handler in Astro or Remix' failure_modes: - mistake: 'Mismatched endpoint path in fetchRequestHandler' mechanism: > The endpoint option must match the actual URL path prefix where the handler is mounted. Mismatches cause all procedures to 404. source: 'www/docs/server/adapters/fetch.mdx' priority: HIGH status: active compositions: [] # ── Real-time ───────────────────────────────────────────────────── - name: 'Subscriptions' slug: 'subscriptions' domain: 'realtime' description: > Set up real-time event streams using async generator subscriptions. SSE (httpSubscriptionLink) is recommended for most use cases. WebSockets (wsLink) only when bidirectional communication or WebSocket-specific features are required. type: core packages: - '@trpc/server' - '@trpc/client' covers: - '.subscription(async function*() { yield })' - 'tracked(id, data) for reconnection' - 'lastEventId for resuming from last event' - 'AbortSignal for cleanup' - 'httpSubscriptionLink (SSE)' - 'wsLink / createWSClient / applyWSSHandler (WebSocket)' - 'keepAlive configuration' - 'connectionParams for auth' tasks: - 'Create an SSE subscription procedure' - 'Set up WebSocket subscriptions with applyWSSHandler' - 'Enable tracked events for reconnection recovery' - 'Authenticate subscription connections' - 'Clean up resources when clients disconnect' failure_modes: - mistake: 'Using Observable instead of async generator' mechanism: > Observable subscriptions are deprecated and will be removed in v12. Use async generator syntax (async function*) instead. wrong_pattern: | import { observable } from '@trpc/server/observable'; .subscription(({ input }) => { return observable((emit) => { emit.next(data); }); }) correct_pattern: | .subscription(async function* ({ input, signal }) { // Set up listener before fetching history const ee = new EventEmitter(); for await (const event of on(ee, 'data', { signal })) { yield event; } }) source: 'packages/server/src/unstable-core-do-not-import/procedureBuilder.ts:425-427' priority: HIGH status: active - mistake: 'Empty string as tracked event ID' mechanism: > tracked() throws if the ID is an empty string because it conflicts with SSE "no id" semantics. wrong_pattern: | yield tracked('', data); correct_pattern: | yield tracked(event.id.toString(), data); source: 'packages/server/src/unstable-core-do-not-import/stream/tracked.ts:39-44' priority: MEDIUM status: active - mistake: 'Fetching history before setting up event listener' mechanism: > If you fetch historical data before setting up the event listener, events emitted between the fetch and listener setup are lost. wrong_pattern: | .subscription(async function* () { const history = await db.getEvents(); // events may fire here yield* history; for await (const event of listener) { yield event; } }) correct_pattern: | .subscription(async function* () { const listener = setupListener(); // listen first const history = await db.getEvents(); yield* history; for await (const event of listener) { yield event; } }) source: 'www/docs/server/subscriptions.md' priority: HIGH status: active - mistake: 'WebSocket subscription stale inputs on reconnect' mechanism: > When a WebSocket reconnects, subscriptions re-send the original input parameters. There is no hook to re-evaluate inputs on reconnect, which can cause stale data. source: 'https://github.com/trpc/trpc/issues/4122' priority: MEDIUM status: active - mistake: 'SSE ping interval >= client reconnect interval' mechanism: > If the server ping interval is >= the client reconnect timeout, the client disconnects thinking the connection is dead before receiving a ping. Server throws if misconfigured. source: 'packages/server/src/unstable-core-do-not-import/stream/sse.ts:96-104' priority: MEDIUM status: active - mistake: 'Sending custom headers with SSE without EventSource polyfill' mechanism: > The native EventSource API does not support custom headers. To send Authorization headers with SSE subscriptions, you must use an EventSource polyfill (e.g., event-source-polyfill) and pass it via the EventSource option on httpSubscriptionLink. source: 'maintainer interview, www/docs/client/links/httpSubscriptionLink.md' priority: HIGH status: active - mistake: 'Choosing WebSocket when SSE would suffice' mechanism: > SSE (httpSubscriptionLink) is recommended for most subscription use cases. WebSockets add complexity (connection management, reconnection, keepalive). Only use wsLink when bidirectional communication or WebSocket-specific features are required. source: 'maintainer interview' priority: MEDIUM status: active compositions: [] # ── Framework integration ───────────────────────────────────────── - name: 'Next.js App Router' slug: 'nextjs-app-router' domain: 'framework-integration' description: > Set up tRPC in Next.js App Router with server components, RSC prefetching, HydrateClient, and the fetch adapter. type: framework packages: - '@trpc/server' - '@trpc/client' - '@trpc/tanstack-react-query' covers: - 'fetchRequestHandler in route.ts' - 'createTRPCOptionsProxy for server components' - 'createTRPCContext for client components' - 'HydrateClient / HydrationBoundary' - 'prefetchQuery / useSuspenseQuery pattern' - 'Server Actions' tasks: - 'Set up tRPC API route handler in app/api/trpc/[trpc]/route.ts' - 'Prefetch data in server components and hydrate to client' - 'Use tRPC with Suspense boundaries' - 'Call tRPC procedures from Server Actions' failure_modes: - mistake: 'Not exporting both GET and POST from route handler' mechanism: > Next.js App Router route handlers must export named GET and POST functions. Missing either causes queries or mutations to 405. wrong_pattern: | // app/api/trpc/[trpc]/route.ts export default function handler(req) { ... } correct_pattern: | // app/api/trpc/[trpc]/route.ts const handler = (req: Request) => fetchRequestHandler({ req, router: appRouter, endpoint: '/api/trpc', createContext }); export { handler as GET, handler as POST }; source: 'www/docs/server/adapters/nextjs.md' priority: CRITICAL status: active - mistake: 'Suspense query failure crashes entire page' mechanism: > If a query fails during SSR with useSuspenseQuery, the entire page crashes even if wrapped in an Error Boundary. Error boundaries only work on the client side for SSR failures. source: 'www/docs/client/react/suspense.md' priority: HIGH status: active - mistake: 'Creating singleton QueryClient for SSR' mechanism: > In server components, each request needs its own QueryClient instance. A singleton QueryClient leaks data between requests. wrong_pattern: | const queryClient = new QueryClient(); // shared across requests! correct_pattern: | function makeQueryClient() { return new QueryClient({ ... }); } source: 'www/docs/client/react/server-components.mdx' priority: CRITICAL status: active - mistake: 'Missing dehydrate/serialize config on QueryClient' mechanism: > RSC hydration requires configuring dehydrate.serializeData and hydrate.deserializeData on the QueryClient for proper data serialization across the server/client boundary. source: 'packages/react-query/src/rsc.tsx' priority: HIGH status: active compositions: - library: 'Next.js' - library: '@tanstack/react-query' - name: 'Next.js Pages Router' slug: 'nextjs-pages-router' domain: 'framework-integration' description: > Set up tRPC in Next.js Pages Router with withTRPC HOC, SSR/SSG helpers, and the Next.js adapter. type: framework packages: - '@trpc/next' - '@trpc/react-query' - '@trpc/client' - '@trpc/server' covers: - 'createTRPCNext()' - 'withTRPC() HOC' - 'createNextApiHandler()' - 'SSR via ssr: true' - 'SSG via createSSGHelpers' - 'getServerSideProps / getStaticProps integration' tasks: - 'Set up tRPC API handler in pages/api/trpc/[trpc].ts' - 'Wrap app with withTRPC HOC' - 'Enable SSR for tRPC queries' - 'Pre-render pages with SSG helpers' failure_modes: - mistake: 'SSR prepass renders multiple times' mechanism: > The SSR prepass loop renders the component tree repeatedly until no queries are fetching. This is expected but can cause performance issues with expensive renders. source: 'packages/next/src/ssrPrepass.ts:47-185' priority: MEDIUM status: active - mistake: 'Using ssr: true without understanding implications' mechanism: > Enabling ssr: true imports react-dom and runs prepass on every request. This adds latency and server load. Use selective prefetching in getServerSideProps instead for better control. source: 'www/docs/client/nextjs/overview.mdx' priority: MEDIUM status: active compositions: - library: 'Next.js' - library: '@tanstack/react-query' # ── Composition skills ──────────────────────────────────────────── - name: 'Auth' slug: 'auth' domain: 'defining-api' description: > Implement authentication and authorization in tRPC using OAuth2.0 with JWT bearer tokens or cookies, auth middleware, and protected procedures. type: composition packages: - '@trpc/server' - '@trpc/client' covers: - 'Auth middleware pattern' - 'Context user extraction from headers/cookies' - 'Protected base procedures (authedProcedure)' - 'Client-side Authorization header / cookie setup' - 'WebSocket connectionParams for auth' - 'SSE auth (cookies, custom headers, connection params)' tasks: - 'Set up JWT bearer token auth in context and middleware' - 'Create an authedProcedure base that narrows user type' - 'Send auth headers from the client' - 'Authenticate WebSocket connections' - 'Authenticate SSE subscription connections' failure_modes: - mistake: 'Not narrowing user type in auth middleware' mechanism: > Without context narrowing via opts.next({ ctx }), downstream procedures still see user as possibly null/undefined, requiring redundant null checks. wrong_pattern: | const authMiddleware = t.middleware(async ({ ctx, next }) => { if (!ctx.user) throw new TRPCError({ code: 'UNAUTHORIZED' }); return next(); // user still nullable downstream }); correct_pattern: | const authMiddleware = t.middleware(async ({ ctx, next }) => { if (!ctx.user) throw new TRPCError({ code: 'UNAUTHORIZED' }); return next({ ctx: { user: ctx.user } }); // narrows to non-null }); source: 'www/docs/server/authorization.md' priority: HIGH status: active - mistake: 'SSE auth via URL query params exposes tokens' mechanism: > connectionParams are sent as query strings for SSE. This exposes tokens in server logs and browser history. Prefer cookies for same-domain or custom headers via EventSource ponyfill. source: 'www/docs/client/links/httpSubscriptionLink.md' priority: HIGH status: active - mistake: 'Async headers causing stuck isFetching' mechanism: > When the headers function is async (e.g., refreshing auth tokens), React Query's isFetching can get stuck permanently in certain race conditions. source: 'https://github.com/trpc/trpc/issues/7001' priority: MEDIUM status: active - mistake: 'Skipping auth or opening CORS too wide in prototypes' mechanism: > Agents prototyping quickly may skip auth middleware or set CORS to "*". These patterns work for development but are dangerous in production. Always add auth and restrict CORS origin to known domains. source: 'maintainer interview' priority: HIGH status: active compositions: - library: 'OAuth2.0 / JWT' - name: 'SuperJSON' slug: 'superjson' domain: 'consuming-api' description: > Configure SuperJSON transformer on both server init and client links to support Date, Map, Set, BigInt, and other non-JSON types. type: composition packages: - '@trpc/server' - '@trpc/client' covers: - 'initTRPC.create({ transformer: superjson })' - 'httpBatchLink({ transformer: superjson })' - 'Symmetric transformer requirement' tasks: - 'Add SuperJSON to an existing tRPC setup' - 'Configure transformer on both server and all client links' failure_modes: - mistake: 'Transformer on server but not on client link' mechanism: > Server serializes with superjson but client expects plain JSON. Error: "Unable to transform response from server." wrong_pattern: | // Server initTRPC.create({ transformer: superjson }); // Client httpBatchLink({ url: '/api/trpc' }); // missing transformer correct_pattern: | // Server initTRPC.create({ transformer: superjson }); // Client httpBatchLink({ url: '/api/trpc', transformer: superjson }); source: 'https://github.com/trpc/trpc/issues/7083' priority: CRITICAL status: active - mistake: 'Error responses bypass transformer' mechanism: > Some error responses (e.g., validation errors) are emitted as plain JSON even when superjson is configured, producing a hybrid wire format that fails deserialization. source: 'https://github.com/trpc/trpc/issues/7083' priority: HIGH status: active - mistake: 'Forgetting transformer on subscription links' mechanism: > httpSubscriptionLink and wsLink also need the transformer. When using splitLink, the transformer must be on every terminating link branch. source: 'www/docs/server/data-transformers.md' priority: HIGH status: active compositions: - library: 'superjson' - name: 'OpenAPI' slug: 'openapi' domain: 'interop' description: > Generate an OpenAPI 3.1 spec from a tRPC router and generate a publishable typed REST client using HeyAPI. type: composition packages: - '@trpc/openapi' - '@trpc/server' covers: - 'trpc-openapi CLI' - 'OpenAPI 3.1 spec generation' - 'HeyAPI client generation' - 'createTRPCHeyApiClientConfig()' - 'Transformer configuration for generated clients' tasks: - 'Generate an OpenAPI document from the tRPC router' - 'Generate a typed REST client with HeyAPI' - 'Configure transformers for the generated client' - 'Publish the generated client as an npm package' failure_modes: - mistake: 'Missing transformer config in HeyAPI client' mechanism: > When the tRPC server uses superjson, the generated HeyAPI client must also be configured with the same transformer via createTRPCHeyApiClientConfig({ transformer }). source: 'www/docs/client/openapi.md' priority: HIGH status: active - mistake: 'Expecting subscriptions in OpenAPI spec' mechanism: > Subscriptions are currently excluded from OpenAPI spec generation. SSE subscription support is planned but not yet available. source: 'www/docs/client/openapi.md' priority: MEDIUM status: active compositions: - library: '@hey-api/openapi-ts' - name: 'Service-oriented architecture' slug: 'service-oriented-architecture' domain: 'interop' description: > Break a tRPC backend into multiple services with cross-service calls and custom routing links. type: composition packages: - '@trpc/server' - '@trpc/client' covers: - 'Custom routing link' - 'Path-based service routing' - 'Shared procedure/router libraries' - 'Gateway pattern' tasks: - 'Split a monolithic tRPC API into multiple services' - 'Create a gateway that routes to backend services' - 'Build a custom link for service-based routing' failure_modes: - mistake: 'Path routing assumes server name prefix' mechanism: > Custom routing links that split on the first path segment break if the router structure changes. The path convention must be documented and enforced. wrong_pattern: | const serverName = op.path.split('.').shift(); // Breaks if router is restructured correct_pattern: | // Use explicit router segments with clear convention const [serverName, ...rest] = op.path.split('.'); const link = servers[serverName]; return link({ ...ctx, op: { ...op, path: rest.join('.') } }); source: 'examples/soa/client/client.ts' priority: MEDIUM status: active compositions: [] tensions: - name: 'Type safety vs runtime flexibility' skills: ['server-setup', 'middlewares'] description: > tRPC allows routers from different initTRPC instances with different context types to be composed without compile-time errors, but they crash at runtime. implication: > An agent may mix procedures from different tRPC instances in a router without realizing the context types are incompatible, producing code that compiles but fails at runtime. - name: 'Batching convenience vs caching safety' skills: ['links', 'caching'] description: > Batching is enabled by default and combines requests. Caching batched responses that include authenticated data can leak personal information to other users via CDN. implication: > An agent adding caching headers to a tRPC endpoint may not realize that batched requests mix public and private data in one response, creating a security vulnerability. - name: 'SSR simplicity vs server component architecture' skills: ['nextjs-app-router', 'nextjs-pages-router'] description: > The Pages Router SSR pattern (ssr: true) is simple but imports react-dom and pre-renders repeatedly. The App Router pattern is more performant but requires manual prefetch/hydrate wiring. implication: > An agent may apply Pages Router SSR patterns in an App Router project, or vice versa, producing code that either doesn't work or has unnecessary overhead. - name: 'Streaming performance vs reliability' skills: ['links', 'subscriptions'] description: > httpBatchStreamLink offers better performance but has known reliability issues on some platforms (React Native, edge runtimes). httpBatchLink is more reliable but doesn't support streaming. implication: > An agent may default to httpBatchStreamLink without considering the target platform's streaming support, causing intermittent failures that are hard to debug. cross_references: - from: 'server-setup' to: 'client-setup' reason: > Server transformer/errorFormatter config must be matched on client. AppRouter type export is consumed by client. - from: 'server-setup' to: 'middlewares' reason: > Base procedures (publicProcedure, authedProcedure) are defined during server setup and compose middleware. - from: 'client-setup' to: 'links' reason: > Client creation requires configuring a link chain. Link choice determines batching, streaming, and subscription behavior. - from: 'client-setup' to: 'superjson' reason: > Transformer must be configured on every terminating link. SuperJSON is the most common transformer choice. - from: 'links' to: 'subscriptions' reason: > Subscription procedures require a specific link type (httpSubscriptionLink or wsLink) routed via splitLink. - from: 'auth' to: 'middlewares' reason: > Auth is typically implemented as middleware that narrows context type to non-nullable user. - from: 'auth' to: 'subscriptions' reason: > Subscription auth requires different mechanisms (connectionParams for WebSocket, cookies/custom headers for SSE) than HTTP auth. - from: 'validators' to: 'error-handling' reason: > Validation failures produce BAD_REQUEST TRPCErrors. Custom errorFormatter is needed to expose field-level Zod errors. - from: 'react-query-setup' to: 'nextjs-app-router' reason: > Next.js App Router uses @trpc/tanstack-react-query with RSC prefetching patterns. - from: 'react-query-classic-migration' to: 'react-query-setup' reason: > Migration target is the new TanStack React Query integration. - from: 'openapi' to: 'superjson' reason: > OpenAPI clients need transformer config matching the server when using superjson or similar. - from: 'non-json-content-types' to: 'links' reason: > Non-JSON inputs require splitLink routing to httpLink (not batch). - from: 'caching' to: 'auth' reason: > Cached responses must not include authenticated data. Auth-aware cache control logic is required. - from: 'nextjs-app-router' to: 'nextjs-pages-router' reason: > Developers may need to use both routers in the same project during migration. Patterns differ significantly. gaps: - skill: 'subscriptions' question: > What are the recommended patterns for scaling WebSocket connections in production (connection pooling, load balancing)? context: > GitHub issues show recurring questions about WebSocket scaling but docs don't address production deployment patterns. status: open - skill: 'nextjs-app-router' question: > What is the recommended caching strategy for tRPC in Next.js App Router given that Cache-Control headers are overridden? context: > Issue #5625 flagged that documented caching approach doesn't work in App Router. No alternative pattern is documented. status: open - skill: 'openapi' question: > What transformer configurations are recommended for OpenAPI clients beyond superjson (e.g., MongoDB EJSON, Amazon Ion)? context: > Test files show mongoEjson and amazonIon tests but no documentation covers these alternatives. status: open - skill: 'service-oriented-architecture' question: > Are there recommended patterns for service discovery, health checks, or circuit breaking in a tRPC SOA setup? context: > The SOA example shows basic routing but production concerns like resilience are not addressed. status: open