CodeVetter · Repo Unpack · Extension points
trpc/trpc
New behavior is meant to plug in at initTRPC factories, the middleware and parser pipeline, client links, and HTTP adapters. Cross-cutting hooks are errorFormatter, transformer, responseMeta, createContext, and onError. Routers can be merged or lazy-loaded. Standard Schema is the validator seam. OpenAPI generation, the upgrade codemods, and the skills directories are the extension points outside the RPC runtime.
- initTRPC.create() is the composition root. It returns five extension factories: procedure (createBuilder), middleware (createMiddlewareFactory), router (createRouterFactory), mergeRouters, and createCallerFactory. Docs and CONTRIBUTING tell app authors to call initTRPC once per backend.
- Middleware is a list, not a class hierarchy. MiddlewareBuilder.unstable_pipe and ProcedureBuilder.use() append functions. next() may override ctx, input, or getRawInput. The marker field on MiddlewareResult is what forces middleware to return next()'s output.
- A client link is a function from TRPCClientRuntime to an OperationLink. createChain is the registry: links run in array order and the last must be a terminating link or it throws 'did you forget to add an ending link?'. splitLink, retryLink, and loggerLink are non-terminating; http*, wsLink, and unstable_localLink terminate.
- New HTTP adapters are expected to import AnyTRPCRouter from @trpc/server and HTTPBaseHandlerOptions from @trpc/server/http, then call resolveResponse. That comment is copied at the top of fetch, express, next, fastify, and standalone adapter files.
- Per-request extension hooks on the HTTP handler are createContext, onError, responseMeta, allowBatching, allowMethodOverride, and maxBatchSize. responseMeta can set status and headers; passing a plain object of headers is deprecated in favor of Headers and scheduled for deletion in v12.
- RootConfig is the place for cross-cutting server plugins that are not procedures: transformer, errorFormatter, sse, jsonl, defaultMeta, isDev, isServer, and allowOutsideOfServer. experimental is an empty object type reserved on the config.
- Validator plugins hook getParseFn. Adding a library means detecting a discriminant (parseAsync, ~standard, validateSync, create, assert) without breaking the function-schema case used by Effect. ProcedureBuilder.input() stores both the raw parser (for types) and the middleware (for runtime).
- Content types plug in as ContentTypeHandler objects with isMatch and parse. The JSON handler is jsonContentTypeHandler. Octet streams plug in via octetInputParser. Batch calls share one parsed input record indexed by batch position.
- Router extension without editing the root object is mergeRouters(...) or lazy(() => import(...)). lazy() requires the module to be the router itself or a module with exactly one router export, otherwise it throws a fixed error string that shows the expected import pattern.
- experimental_caller on the procedure builder replaces the exported function while keeping _def and the original invoke. That is the seam for custom server-side call conventions. createCallerFactory is the supported in-process API and is what app routers re-export.
- WebSocket extension options on applyWSSHandler include prefix, keepAlive.enabled, keepAlive.pingMs, and keepAlive.pongWaitMs. An encoder seam exists as adapters/wsEncoder.ts with jsonEncoder imported by ws.ts. Fastify opts into it with useWSS on fastifyTRPCPlugin.
- OpenAPI extension is GenerateOptions (exportName, title, version, servers) passed to generateOpenAPIDocument, which walks TypeScript types rather than executing procedures. Upgrade extension is a new jscodeshift transform registered in the multiselect in packages/upgrade/src/bin/index.ts and exported from package exports ./transforms/*.