CodeVetter · Repo Unpack · Behavior traces
trpc/trpc
Startup is initTRPC.create once per backend, then router({...}) which flattens procedures. An HTTP query or mutation enters through an adapter, resolveResponse, getRequestInfo, one context factory, the procedure middleware stack, and transformTRPCResponse. A chat send is a mutation that writes Drizzle and an SSE subscription that replays and tails an emitter. In-process calls skip HTTP via createCaller or unstable_localLink.
- Server startup in the Prisma starter: initTRPC.context<Context>().create({transformer, errorFormatter}) returns router and publicProcedure; appRouter = router({healthcheck, post: postRouter}); pages/api/trpc/[trpc].ts default-exports createNextApiHandler({router: appRouter, createContext, onError}). No listen() call exists in that file because Next invokes the handler.
- HTTP query order: adapter (fetchRequestHandler or createNextApiHandler to nodeHTTPRequestHandler) calls resolveResponse; HEAD returns 204; getRequestInfo parses path and inputs; ctxManager.create(info) runs createContext once; the procedure function from createProcedureCaller runs callRecursive from middleware index 0 through input parsers to the resolver; non-stream results go through getErrorShape or {result:{data}}, initResponse, transformTRPCResponse, and new Response.
- Client query order: user calls proxy path ending in .query; createTRPCClientProxy maps it to TRPCUntypedClient.query; $request builds an Operation and createChain walks links; httpBatchLink's query dataLoader.load joins the batch and jsonHttpRequester fetches; transformResult applies transformer.output; requestAsPromise returns result.data.
- Prisma post.add trace: publicProcedure.input(zod).mutation receives {ctx, input}; prisma.post.create({data: input, select: defaultPostSelect}) runs; the row is returned as result.data. list uses cursor pagination (take limit+1, pop the extra row into nextCursor) and returns {items, nextCursor}.
- SSE chat send trace: POST /api/trpc hits the route handler, fetchRequestHandler, resolveResponse. createContext loads the NextAuth session. authedProcedure checks user.name. post.add inserts into Drizzle Post, clears currentlyTyping, ee.emit('add', channelId, post). Subscribers in post.onAdd already hold ee.toIterable('add') and yield tracked(post.id, post) after filtering by channelId.
- SSE reconnect trace inside post.onAdd: input.lastEventId selects that Post's createdAt, a findMany loads newer rows for the channel, they are yielded first, then the function for-awaits the emitter. Root config sets sse.maxDurationMs to 5 minutes, ping every 3s, and client.reconnectAfterInactivityMs of 5s. resolveResponse arms a timer that aborts the combined signal when maxDurationMs elapses.
- In-process caller trace: createCallerFactory(appRouter) then caller(ctx).procedure(input) hits createRecursiveProxy, getProcedureAtPath, and procedure({getRawInput: async () => args[0], ctx, type, signal, batchIndex: 0}). onError receives the TRPCError and the original cause is rethrown. unstable_localLink is the client-side equivalent and calls callProcedure without a socket.
- Lazy router trace: lazy(importRouter) marks the function with lazyMarker. createRouterFactory records a LazyLoader instead of walking the record. getProcedureAtPath, if the path is missing, finds a lazy key that is a prefix, awaits loader.load(), which imports the module (must be a router or a module with exactly one router export) and splices it into the aggregate record.
- WebSocket server trace starts at applyWSSHandler(opts) in adapters/ws.ts. It uses parseTRPCMessage from @trpc/server/rpc, callTRPCProcedure, getErrorShape, and transformTRPCResponse. keepAlive defaults to a ping every 30_000ms and pong wait 5_000ms when enabled. fastifyTRPCPlugin can attach the same WS handler when useWSS is set.
- Dev loop from CONTRIBUTING: pnpm install && pnpm build, then pnpm dev watches packages/* via turbo. A second terminal runs an example such as examples/next-prisma-starter pnpm dev against the workspace packages. pnpm test filters by substring, including GitHub issue numbers.