inferRouterInputs and inferRouterOutputs helpers
The `inferRouterInputs` and `inferRouterOutputs` helpers are exported from `@trpc/server` and can be used to infer input and output types for all procedures in your router. They are commonly exported as type aliases (e.g., `export type RouterInputs = inferRouterInputs<AppRouter>;` and `export type RouterOutputs = inferRouterOutputs<AppRouter>;`) for use in React components and custom hooks.
inferInput and inferOutput for single procedure types
Use inferInput and inferOutput from @trpc/tanstack-react-query to infer types for a single procedure. Pass the procedure proxy from useTRPC as the generic argument.
inferRouterInputs and inferRouterOutputs for full router types
Use inferRouterInputs and inferRouterOutputs from @trpc/server to infer the input and output types for a full router. These are generic functions that accept the AppRouter type and return mapped types of all inputs and outputs.
Type inference from AppRouter example
To infer types from an AppRouter, first export the router type as type AppRouter = typeof appRouter from your server file. Then on the client, import both inferRouterInputs/inferRouterOutputs and the AppRouter type, and use them to access nested input and output types for any procedure.
inferProcedureOutput helper type
The inferProcedureOutput<TProcedure> helper type exported from @trpc/server allows you to infer the output type of a specific procedure directly without inferring all outputs from the router. Example: type PostByIdOutput = inferProcedureOutput<AppRouter['post']['byId']>.
inferSubscriptionOutput helper type
The inferSubscriptionOutput<TProcedure> helper type exported from @trpc/server allows you to infer the type of data emitted by a subscription procedure. Example: type OnPostAddOutput = inferSubscriptionOutput<AppRouter['post']['onPostAdd']>.
Type guard for TRPCClientError with AppRouter
You can create a type guard function to check if an error is a TRPCClientError typed to your specific AppRouter. The function uses instanceof TRPCClientError and returns a type predicate: cause is TRPCClientError<AppRouter>. When the guard returns true, the error is properly typed and you can access properties like cause.data.
inferRouterOutputs helper type
The inferRouterOutputs<TRouter> helper type exported from @trpc/server allows you to infer all output types from an AppRouter. Access specific procedure outputs using nested property access, for example type PostCreateOutput = inferRouterOutputs<AppRouter>['post']['create'].
tRPC client uses JavaScript Proxy
The tRPC client creates a typed JavaScript Proxy under the hood, allowing type-safe interaction with the tRPC API. This enables autocomplete and type checking on all procedure calls.
tRPC UI testing and development tools
Community tools for testing and development include: tRPC-ui (automatically generates a UI for manually testing tRPC backend), tRPC Playground (sandbox for testing tRPC queries in the browser), tRPC Client Devtools (browser extension), and tRPC Docs Generator (auto generates interactive documentation).
tRPC CLI tool
trpc-cli turns a tRPC router into a type-safe, fully documented CLI tool.
tRPC adapter for MCP (Model Context Protocol)
trpc-to-mcp allows turning a tRPC router into MCP tools, server and handler.
Frontend framework integrations for tRPC
tRPC has community integrations for multiple frontend frameworks: tRPC-SvelteKit for SvelteKit, tRPC-Remix for Remix, tRPC Client For SolidJS with Solid Query, tRPC API Handler for SolidStart, and tRPC-nuxt for Nuxt 3.
tRPC testing integration with MSW
msw-trpc provides tRPC support for Mock Service Worker (MSW) testing.
tRPC real-world production projects
Notable open-source projects using tRPC include: Cal.com (scheduling infrastructure), SST (serverless framework), Beam (message board by PlanetScale), Rallly (doodle poll alternative), Answer Overflow (Discord bot), Tianji (analytics and monitoring hub), Dotfyle (Neovim plugin discovery), and ConvoForm (AI-powered conversational forms).
tRPC community extensions and add-ons overview
The tRPC ecosystem includes numerous community-built extensions and add-ons organized into categories: Extensions (documentation generators, UI testing tools, OpenAPI support, devtools, CLI tools, permission systems), Frontend frameworks (SvelteKit, Remix, SolidJS, Nuxt), Bootstrappers (create-t3-app, sidebase, viteRPC), Library adapters (Jotai, Zustand, serverless adapters, RTK Query, SWR), and Server runtime adapters (Bun, Koa, uWebSockets, RabbitMQ, MQTT, Redis).
Export AppRouter type for end-to-end type safety
Export the type of the router using 'export type AppRouter = typeof appRouter' to enable end-to-end type inference on the client side.
Export AppRouter type for client-side type inference
Export the type of your router as AppRouter (or similar name) using typeof. This type is then imported on the client side and passed to createTRPCClient as a generic parameter to enable full end-to-end type inference between client and server.
Calling a tRPC query from the client
To call a query procedure from the client, use the trpc object to navigate to the procedure (e.g., trpc.greeting.query()) and pass input that matches the server's input schema. The input is type-checked at compile time based on the AppRouter type.
Batching response with mixed status codes
When batch responses have different HTTP status codes (e.g., one call succeeded and one failed), tRPC returns HTTP 207 Multi-Status.
HTTP method to tRPC procedure mapping
GET requests map to .query() procedures, POST requests map to .mutation() procedures, and GET requests also map to .subscription() procedures via Server-sent Events (httpSubscriptionLink) or WebSockets (wsLink).
Query input encoding in GET requests
For query procedures using GET, input is JSON-stringified and passed as a query parameter. The format is: myQuery?input=${encodeURIComponent(JSON.stringify(input))}
Mutation input in POST requests
For mutation procedures using POST, input is sent as the POST body.
Batching multiple procedures in one HTTP request
Multiple parallel procedure calls of the same HTTP method are combined into one request using a data loader. Procedure names are joined by commas in the pathname (e.g., /api/trpc/postById,relatedPosts), input parameters are sent as a query parameter called 'input' with shape Record<number, unknown>, and batch=1 must be passed as a query parameter.
Batching input parameter format
When batching, the input query parameter is a Record where keys are numeric indices (0, 1, 2, etc.) corresponding to the position of each procedure call, and values are the inputs for those procedures. Example: encodeURIComponent(JSON.stringify({ 0: '1', 1: '1' }))
Successful tRPC HTTP response format
Successful responses follow the format: { result: { data: TOutput } } where TOutput is the output from the procedure.
Error tRPC HTTP response format
Error responses follow the format: { error: { json: { message: string, code: number (JSON-RPC 2.0 code), data: { code: string, httpStatus: number, stack?: string, path: string } } } }. The data object contains customizable metadata about the error.
Error codes to HTTP status mapping
tRPC error codes map to HTTP status codes as follows: PARSE_ERROR: 400, BAD_REQUEST: 400, UNAUTHORIZED: 401, PAYMENT_REQUIRED: 402, FORBIDDEN: 403, NOT_FOUND: 404, METHOD_NOT_SUPPORTED: 405, TIMEOUT: 408, CONFLICT: 409, PRECONDITION_FAILED: 412, PAYLOAD_TOO_LARGE: 413, UNSUPPORTED_MEDIA_TYPE: 415, UNPROCESSABLE_CONTENT: 422, PRECONDITION_REQUIRED: 428, TOO_MANY_REQUESTS: 429, CLIENT_CLOSED_REQUEST: 499, INTERNAL_SERVER_ERROR: 500, NOT_IMPLEMENTED: 501, BAD_GATEWAY: 502, SERVICE_UNAVAILABLE: 503, GATEWAY_TIMEOUT: 504
Error codes to JSON-RPC 2.0 error code mapping
tRPC error codes map to JSON-RPC 2.0 codes as follows: PARSE_ERROR: -32700, BAD_REQUEST: -32600, INTERNAL_SERVER_ERROR: -32603, NOT_IMPLEMENTED: -32603, BAD_GATEWAY: -32603, SERVICE_UNAVAILABLE: -32603, GATEWAY_TIMEOUT: -32603, UNAUTHORIZED: -32001, PAYMENT_REQUIRED: -32002, FORBIDDEN: -32003, NOT_FOUND: -32004, METHOD_NOT_SUPPORTED: -32005, TIMEOUT: -32008, CONFLICT: -32009, PRECONDITION_FAILED: -32012, PAYLOAD_TOO_LARGE: -32013, UNSUPPORTED_MEDIA_TYPE: -32015, UNPROCESSABLE_CONTENT: -32022, PRECONDITION_REQUIRED: -32028, TOO_MANY_REQUESTS: -32029, CLIENT_CLOSED_REQUEST: -32099
HTTP status propagation from errors
tRPC propagates HTTP status codes from errors when possible. When responses have different statuses across multiple calls, HTTP 207 Multi-Status is returned.
Overriding HTTP method for queries and mutations
The HTTP method used for queries and mutations can be overridden using the methodOverride option. On the server, set allowMethodOverride: true in createHTTPHandler(). On the client, specify methodOverride: 'POST' in httpLink() to send all queries and mutations as POST requests.
Batching example with two queries
Example showing batching of postById.useQuery('1') and relatedPosts.useQuery('1') in a React component results in a single HTTP GET request to /api/trpc/postById,relatedPosts?batch=1&input=%7B%220%22%3A%221%22%2C%221%22%3A%221%22%7D with response as an array of result objects.
RPC definition and concept
RPC stands for Remote Procedure Call. It is a way of calling functions on one computer (the server) from another computer (the client). With RPC, you call a function and get a response, rather than calling a URL as with traditional HTTP/REST APIs.
Ignore HTTP implementation details in tRPC
When writing tRPC application code, you should not think about HTTP/REST implementation details such as HTTP verbs. tRPC handles these automatically. Form your function names based on the action (for example, 'getUser(id)') rather than REST conventions (like 'GET /users/:id').
tRPC function call example vs HTTP/REST
With RPC in tRPC, instead of calling a URL like 'fetch(/api/users/1)', you call a function like 'api.users.getById({ id: 1 })'. This is the fundamental difference in how the client interface works compared to traditional HTTP/REST.
tRPC is RPC implementation for TypeScript
tRPC (TypeScript Remote Procedure Call) is one implementation of RPC, designed for TypeScript monorepos. While it uses standard HTTP requests and responses under the hood, the API is function-based rather than URL-based.
tRPC handles type safety without code generation
In full-stack TypeScript projects, tRPC keeps API contracts in sync between client and server by leveraging TypeScript's type inference directly, with no code generation step, catching problems at build time.
End-to-end type safety with AppRouter type import
Import the AppRouter type on the client side using type-only imports to achieve full end-to-end type safety without leaking implementation details. Type-only imports are stripped at build time.
Client procedure call with type inference
After setting up the tRPC client with AppRouter type, call procedures using trpc.procedureName.query() for queries or trpc.procedureName.mutate() for mutations. The input and output types are fully inferred from the server definition.
Client procedure call examples with type inference
const user = await trpc.userById.query('1');
const createdUser = await trpc.userCreate.mutate({ name: 'Katt' });
Export AppRouter type from server
Export the AppRouter type from your server router file using `export type AppRouter = typeof appRouter;`. This creates a type that represents the shape of your entire API and can be imported on the client.
Import AppRouter type on client with import type
Import the AppRouter type on the client side using `import type { AppRouter } from '../server/router';`. Using `import type` ensures the reference is stripped at compile-time, preventing server-side code from being inadvertently imported into the client bundle.
AppRouter type holds shape of entire API
The AppRouter type holds the shape of your entire API, including all routers and procedures. This type is used on the client to provide end-to-end type safety for API calls.
Example: Export and import AppRouter type
Server: `export type AppRouter = typeof appRouter;` where appRouter is your t.router({...}). Client: `import type { AppRouter } from '../server/router';`
createSSGHelpers renamed
createSSGHelpers is now the primary export. createProxySSGHelpers has been renamed to createSSGHelpers and the old v9 createSSGHelpers has been removed. createProxySSGHelpers is deprecated but aliased to createSSGHelpers for backwards compatibility.
Procedure output type inference simplified
Procedures in routers now only emit their input and output types. Previously they also contained the full context object for every procedure, leading to unnecessary complexity in .d.ts outputs and type inference.
Deleted type exports inferHandlerInput and ProcedureArgs
The types inferHandlerInput and ProcedureArgs have been deleted. Use inferProcedureInput<TProcedure> instead and TRPCProcedureOptions for options.
Procedure._def internal types moved to $types
Procedure._def._output_in and Procedure._def._input_in have been moved to Procedure._def.$types. This is a breaking change for tRPC internals but should not affect users unless directly accessing these private properties.
Middleware context narrowing with type safety
When using middleware for authorization, calling opts.next() with a modified context object allows TypeScript to narrow the context type. For example, if ctx.user is nullable initially, the middleware can return a context where user is non-null, so procedures using this middleware have user as a required property.
inferProcedureBuilderResolverOptions type helper
inferProcedureBuilderResolverOptions is a type helper that infers the options type of a specific procedure builder or base procedure. It is useful for declaring types to function parameters, such as when separating a procedure's handler from its definition or creating helper functions that work with multiple procedures.
Example of using inferProcedureBuilderResolverOptions
A helper function can be typed using inferProcedureBuilderResolverOptions<typeof procedureName> to correctly type the opts parameter, which will have properly typed ctx and input properties that can then be used in the function body.
Export only AppRouter type to the client
When defining an appRouter, export only its type (export type AppRouter = typeof appRouter) rather than the router instance itself. This prevents client code from importing server implementation details.
Procedure-level type inference with inferInput and inferOutput
Import inferInput and inferOutput from '@trpc/tanstack-react-query'. Inside a component, use them with the useTRPC hook: type Input = inferInput<typeof trpc.user.byId>; type Output = inferOutput<typeof trpc.user.byId>.
Router-level type inference with inferRouterInputs and inferRouterOutputs
Import inferRouterInputs and inferRouterOutputs from '@trpc/server'. Use them to infer all inputs and outputs for a router: type Inputs = inferRouterInputs<AppRouter>; type Outputs = inferRouterOutputs<AppRouter>. Access nested types with dot notation: type UserInput = Inputs['user']['byId'].
tRPC end-to-end type inference
Export AppRouter type from server using export type AppRouter = typeof appRouter. Pass this type as a generic to createTRPCClient on the client for full end-to-end type safety.
Type checking with tRPC React example
The minimal React tRPC example demonstrates type checking by editing TypeScript files and seeing the type checker validate the changes.
E2E type safety with tRPC
tRPC provides end-to-end type safety across full-stack applications