Move Fast and Break Nothing.End-to-end typesafe APIs made easy.
Experience the full power of TypeScript inference to boost productivity for your full-stack application.
$ pnpm add @trpc/server @trpc/client
Experience the full power of TypeScript inference: write procedures on the server and call them from the client with their types. No schemas to generate, no code to sync. v12 is rebuilt on Effect, and you don't have to use it.
$ pnpm add @trpc12/trpc effectNot on npm yet
Supported by
const const trpc: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: `Hello ${string}`;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>>
trpc = createTRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: `Hello ${string}`;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>>(opts: CreateTRPCClientOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: `Hello ${string}`;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>>): TRPCClient<...>
createTRPCClient<type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: `Hello ${string}`;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>> & DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: `Hello ${string}`;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>
AppRouter>({
links: TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: `Hello ${string}`;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>>[]
links: [
httpBatchLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: `Hello ${string}`;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>>(opts: HTTPBatchLinkOptions<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>): TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<...>>>
httpBatchLink({
url: string | URLurl: 'http://localhost:3000',
}),
],
});
const res = await const trpc: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: `Hello ${string}`;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>>
trpc.greeting: {
query: Resolver<{
input: {
name: string;
};
output: `Hello ${string}`;
errorShape: DefaultErrorShape;
transformer: false;
}>;
}
greeting.query: (input: {
name: string;
}, opts?: TRPCProcedureOptions) => TRPCTaggedPromise<`Hello ${string}`, DefaultErrorShape>
query({ name: stringname: 'John' });import { function createTrpcClient<TSource, const TLinks extends ReadonlyArray<TrpcLink<any, RouterOf<TSource>>>>(opts: TypedClientOptions<TSource, TLinks> & DuplicateSwitchCheck<TLinks> & UnmetNeedCheck<TLinks>): TrpcClient<RouterOf<TSource>, LinksContext<TLinks>, undefined, LinksTypes<TLinks>>createTrpcClient, function httpLink<TOpts extends HttpLinkOptions>(opts: TOpts): TrpcLink<HttpLinkDecl<TOpts>>httpLink, function routerType<TRouter>(): RouterType<TRouter>routerType } from '@trpc12/trpc/client';
import type { type AppRouter = {
post: {
byId: Procedure<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: {
userId: string | null;
};
meta: {};
input: {
title: string;
};
output: Post;
errors: SchemaValidationError<ZodObject<{
title: ZodString;
}, $strip>>;
services: never;
}>;
};
}
AppRouter } from './server.ts';
const const client: TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: {
userId: string | null;
};
meta: {};
input: {
title: string;
};
output: Post;
errors: SchemaValidationError<ZodObject<{
title: ZodString;
}, $strip>>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>
client = createTrpcClient<RouterType<{
post: {
byId: Procedure<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: {
userId: string | null;
};
meta: {};
input: {
title: string;
};
output: Post;
errors: SchemaValidationError<ZodObject<{
title: ZodString;
}, $strip>>;
services: never;
}>;
};
}>, readonly [...]>(opts: TypedClientOptions<...>): TrpcClient<...>
createTrpcClient({
TypedClientOptions<RouterType<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<...>; services: never; }>; create: Procedure<...>; }; }>, readonly [...]>.router: RouterType<{
post: {
byId: Procedure<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: {
userId: string | null;
};
meta: {};
input: {
title: string;
};
output: Post;
errors: SchemaValidationError<ZodObject<{
title: ZodString;
}, $strip>>;
services: never;
}>;
};
}>
router: routerType<{
post: {
byId: Procedure<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: {
userId: string | null;
};
meta: {};
input: {
title: string;
};
output: Post;
errors: SchemaValidationError<ZodObject<{
title: ZodString;
}, $strip>>;
services: never;
}>;
};
}>(): RouterType<...>
routerType<type AppRouter = {
post: {
byId: Procedure<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: {
userId: string | null;
};
meta: {};
input: {
title: string;
};
output: Post;
errors: SchemaValidationError<ZodObject<{
title: ZodString;
}, $strip>>;
services: never;
}>;
};
}
AppRouter>(),
TypedClientOptions<RouterType<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<...>; services: never; }>; create: Procedure<...>; }; }>, readonly [...]>.links: readonly [TrpcLink<{
form: JsonForm;
provides: "response";
}, any>]
links: [httpLink<{
url: string;
}>(opts: {
url: string;
}): TrpcLink<{
form: JsonForm;
provides: "response";
}, any>
httpLink({ url: stringurl: '/trpc' })],
});
const post = await const client: TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: {
userId: string | null;
};
meta: {};
input: {
title: string;
};
output: Post;
errors: SchemaValidationError<ZodObject<{
title: ZodString;
}, $strip>>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<...>>
client.post: ClientRouter<{
byId: Procedure<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}>;
create: Procedure<{
type: "mutation";
ctx: {
userId: string | null;
};
meta: {};
input: {
title: string;
};
output: Post;
errors: SchemaValidationError<ZodObject<{
title: ZodString;
}, $strip>>;
services: never;
}>;
}, {}, undefined, LinksTypes<...>>
post.byId: {
query(input: {
id: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
id: string;
title: string;
}, CallError<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}, JsonForm, never>>;
}
byId.function query(input: {
id: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
id: string;
title: string;
}, CallError<{
type: "query";
ctx: {
userId: string | null;
};
meta: {};
input: {
id: string;
};
output: Post;
errors: TrpcError<"NOT_FOUND", {
id: string;
}> | SchemaValidationError<ZodObject<{
id: ZodString;
}, $strip>>;
services: never;
}, JsonForm, never>>
query({ id: stringid: '1' });The client of the router below: the types come from the server, with nothing generated.
How it works
It's quick and easy to get started with tRPC to build a typesafe API.
Define your procedures
Define proceduresThe first step to creating a tRPC API is to define your procedures.
Procedures are the functions we will use to build your backend. They're composable and can be queries, mutations, or subscriptions. Routers contain multiple procedures.
In this procedure, we use a Zod validator to ensure the input from the client has exactly the shape that our procedure expects. We will also return a simple text string from the query.
At the end of the file, we export the type of the router so we can use it in our frontend code in just a few moments.
constt =const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>const initTRPC: TRPCBuilder<object, object>initTRPC.create(); constTRPCBuilder<object, object>.create<RuntimeConfigOptions<object, object>>(opts?: RuntimeConfigOptions<object, object> | undefined): TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>router =const router: RouterBuilder<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>t.const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>router; constTRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>publicProcedure =t.const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>procedure; constappRouter =const appRouter: BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>>router({const router: RouterBuilder <{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>(_: { greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }) => BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>>greeting:greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>const publicProcedure: ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>publicProcedure .input(ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.input<z.ZodObject<{ name: z.ZodString; }, z.core.$strip>>(schema: z.ZodObject<{ name: z.ZodString; }, z.core.$strip>): ProcedureBuilder<object, object, object, { name: string; }, { name: string; }, UnsetMarker, UnsetMarker, false, DefaultErrorShape>import zz.object({function object<{ name: z.ZodString; }>(shape?: { name: z.ZodString; } | undefined, params?: string | { error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueUnrecognizedKeys | z.core.$ZodIssueInvalidType<unknown>>> | undefined; message?: string | undefined | undefined; } | undefined): z.ZodObject<{ name: z.ZodString; }, z.core.$strip>name: z.ZodStringname:import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() })) .query((ProcedureBuilder<object, object, object, { name: string; }, { name: string; }, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<`Hello ${string}`>(resolver: ProcedureResolver<object, object, object, { name: string; }, UnsetMarker, `Hello ${string}`>): QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>opts) => { const { input } =opts: ProcedureResolverOptions<object, object, object, { name: string; }>opts; return `Hello ${opts: ProcedureResolverOptions<object, object, object, { name: string; }>input.const input: { name: string; }name: stringname}` astype const = `Hello ${string}`const; }), }); export typeAppRouter = typeoftype AppRouter = Router<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>> & DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>appRouter;const appRouter: BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>>Create your HTTP server
AdaptersNext, we create our HTTP server using our
appRouter. We now have a tRPC server running!tRPC has many adapters so it can meet you where you are. Next.js, Express, the Fetch API (Astro, Remix, SvelteKit, Cloudflare Workers, etc.), Fastify, AWS Lambda, or a vanilla Node HTTP server.
const {const listen: http.Server<any, any>listen } =createHTTPServer({createHTTPServer<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>>(opts: CreateHTTPHandlerOptions<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>>): http.Server<any, any>router:BaseHandlerOptions<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>, http.IncomingMessage>.router: BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>appRouter, }); // The API will now be listening on port 3000!const appRouter: BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>const listen: http.Server<any, any>listen(3000);Connect your client and start querying!
The vanilla clientNow that we have the server running, we can create a client and start querying data.
We pass the
AppRoutertype when creating the client to give us TypeScript autocompletion and Intellisense that matches the backend API without requiring any code generation!consttrpc =const trpc: TRPCClient<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>>>createTRPCClient<createTRPCClient<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>>>(opts: CreateTRPCClientOptions<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>>>): TRPCClient<...>AppRouter>({type AppRouter = Router<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>> & DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>links: [links: TRPCLink<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>>>[]httpBatchLink({httpBatchLink<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>>>(opts: HTTPBatchLinkOptions<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>): TRPCLink<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<...>>>url: string | URLurl: 'http://localhost:3000', }), ], }); const res = awaittrpc.const trpc: TRPCClient<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: `Hello ${string}`; meta: object; errorShape: DefaultErrorShape; }>; }>>>greeting.greeting: { query: Resolver<{ input: { name: string; }; output: `Hello ${string}`; errorShape: DefaultErrorShape; transformer: false; }>; }query({query: (input: { name: string; }, opts?: TRPCProcedureOptions) => TRPCTaggedPromise<`Hello ${string}`, DefaultErrorShape>name: stringname: 'John' });
Three files' worth of code, compiled when this site builds: the types you hover are the ones your editor shows.
Define procedures
ProceduresA router is a plain object of procedures. Each one validates its input with any Standard Schema library and returns its output, or an error with typed data.
Export the router's
type: it is all the client needs.src/server.tsconstt =const t: TrpcRoot<{ userId: string | null; }, {}, RootDef<{ userId: string | null; }, {}>>initTrpc<{initTrpc<{ ctx: { userId: string | null; }; }>(opts?: RootOptions<{}>): TrpcRoot<{ userId: string | null; }, {}, RootDef<{ userId: string | null; }, {}>>ctx: {ctx: { userId: string | null; }userId: string | nulluserId: string | null } }>(); export constappRouter = {const appRouter: { post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>; services: never; }>; }; }post: {post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>; services: never; }>; }byId:byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>; services: never; }>t.const t: TrpcRoot<{ userId: string | null; }, {}, RootDef<{ userId: string | null; }, {}>>procedure .TrpcRoot<{ userId: string | null; }, {}, RootDef<{ userId: string | null; }, {}>>.procedure: ProcedureBuilder<RootDef<{ userId: string | null; }, {}>>input(ProcedureBuilder<RootDef<{ userId: string | null; }, {}>>.input<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>(schema: z.ZodObject<{ id: z.ZodString; }, z.core.$strip>): ProcedureBuilder<InputDef<RootDef<{ userId: string | null; }, {}>, z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>> (+1 overload)import zz.object({function object<{ id: z.ZodString; }>(shape?: { id: z.ZodString; } | undefined, params?: string | { error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined; message?: string | undefined | undefined; } | undefined): z.ZodObject<{ id: z.ZodString; }, z.core.$strip>id: z.ZodStringid:import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() })) .query( (ProcedureBuilder<InputDef<RootDef<{ userId: string | null; }, {}>, ZodObject<{ id: ZodString; }, $strip>>>.query<Post | TrpcError<"NOT_FOUND", { id: string; }>>(resolver: (opts: ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>>) => Post | TrpcError<"NOT_FOUND", { id: string; }>): Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>; services: never; }>opts) =>opts: ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>>const posts: Map<string, Post>posts.Map<string, Post>.get(key: string): Post | undefinedget(opts.opts: ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>>input.ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, ZodObject<{ id: ZodString; }, $strip>>>.input: { id: string; }id: stringid) ??error({error<"NOT_FOUND", { id: string; }>(opts: Omit<TrpcErrorOptions<"NOT_FOUND", { id: string; }>, "defined">): TrpcError<"NOT_FOUND", { id: string; }>code: "NOT_FOUND"code: 'NOT_FOUND',data: {data?: { id: string; } | undefinedid: stringid:opts.opts: ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>>input.ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, ZodObject<{ id: ZodString; }, $strip>>>.input: { id: string; }id: stringid } }), ),create:create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>; services: never; }>t.const t: TrpcRoot<{ userId: string | null; }, {}, RootDef<{ userId: string | null; }, {}>>procedure .TrpcRoot<{ userId: string | null; }, {}, RootDef<{ userId: string | null; }, {}>>.procedure: ProcedureBuilder<RootDef<{ userId: string | null; }, {}>>input(ProcedureBuilder<RootDef<{ userId: string | null; }, {}>>.input<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>(schema: z.ZodObject<{ title: z.ZodString; }, z.core.$strip>): ProcedureBuilder<InputDef<RootDef<{ userId: string | null; }, {}>, z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>> (+1 overload)import zz.object({function object<{ title: z.ZodString; }>(shape?: { title: z.ZodString; } | undefined, params?: string | { error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined; message?: string | undefined | undefined; } | undefined): z.ZodObject<{ title: z.ZodString; }, z.core.$strip>title: z.ZodStringtitle:import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string()._ZodString<$ZodStringInternals<string>>.min(minLength: number, params?: string | z.core.$ZodCheckMinLengthParams): z.ZodStringmin(1) })) .mutation((ProcedureBuilder<InputDef<RootDef<{ userId: string | null; }, {}>, ZodObject<{ title: ZodString; }, $strip>>>.mutation<Post>(resolver: (opts: ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>>) => Post): Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>; services: never; }>opts) => { constopts: ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>>const post: Postpost: Post = {Post.id: stringid:var crypto: Cryptocrypto.Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}`randomUUID(),Post.title: stringtitle:opts.opts: ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>>input.ResolverOpts<InputDef<RootDef<{ userId: string | null; }, {}>, ZodObject<{ title: ZodString; }, $strip>>>.input: { title: string; }title: stringtitle };const posts: Map<string, Post>posts.Map<string, Post>.set(key: string, value: Post): Map<string, Post>set(const post: Postpost.Post.id: stringid,const post: Postpost); returnconst post: Postpost; }), }, }; export typeAppRouter = typeoftype AppRouter = { post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>; services: never; }>; }; }appRouter;const appRouter: { post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>; services: never; }>; }; }Serve them anywhere
AdapterscreateFetchHandlerserves the router over HTTP, SSE and WebSockets. Handhandler.fetchto Workers, Bun, Deno or a Next.js route, or serve it on Node with Effect'sHttpServer.src/server.tsexport constconst handler: FetchHandler<undefined>handler =createFetchHandler({createFetchHandler<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>; services: never; }>; }; }, undefined>(opts: FetchHandlerOptions<...>): FetchHandler<...>router:BaseHandlerOptions<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<...>>; services: never; }>; create: Procedure<...>; }; }>.router: { post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>; services: never; }>; }; }appRouter,const appRouter: { post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<z.ZodObject<{ id: z.ZodString; }, z.core.$strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<z.ZodObject<{ title: z.ZodString; }, z.core.$strip>>; services: never; }>; }; }createContext: (createContext: (opts: CreateContextOpts<undefined>) => MaybePromise<{ userId: string | null; }>opts: CreateContextOpts<undefined>opts) => ({userId: string | nulluserId:opts: CreateContextOpts<undefined>opts.CreateContextOpts<undefined>.request: HttpServerRequestrequest.HttpIncomingMessage<HttpServerError>.headers: Headersheaders['x-user-id'] ?? null, }), }); // Workers, Bun, Deno, Next.js route handlers: any fetch runtime. export default {fetch: (request: Request) => Promise<Response>fetch:const handler: FetchHandler<undefined>handler.FetchHandler<undefined>.fetch: (request: Request) => Promise<Response>fetch };Call them with their types
The clientThe client is typed from
routerType<AppRouter>(), so no server code ships to the browser. Hover a name to see its type.safe()turns a call into a result tuple whose error lists every way it can fail.matchError()makes you handle each of the procedure's errors.src/client.tsimport {function createTrpcClient<TSource, const TLinks extends ReadonlyArray<TrpcLink<any, RouterOf<TSource>>>>(opts: TypedClientOptions<TSource, TLinks> & DuplicateSwitchCheck<TLinks> & UnmetNeedCheck<TLinks>): TrpcClient<RouterOf<TSource>, LinksContext<TLinks>, undefined, LinksTypes<TLinks>>createTrpcClient,function httpLink<TOpts extends HttpLinkOptions>(opts: TOpts): TrpcLink<HttpLinkDecl<TOpts>>httpLink,function routerType<TRouter>(): RouterType<TRouter>routerType } from '@trpc12/trpc/client'; import {function matchError<E extends AnyTrpcError, H extends ErrorHandlers<E> & { readonly [K in keyof H]: K extends HandlerKey<E> ? unknown : never; }>(err: E, handlers: H): HandlerResult<H>matchError,safe } from '@trpc12/trpc/client'; import type {function safe<T, E = unknown>(promise: PromiseLike<T> & { readonly "~error"?: E; }): Promise<SafeResult<T, E>>AppRouter } from './server.ts'; consttype AppRouter = { post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }; }client =const client: TrpcClient<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }; }, {}, undefined, LinksTypes<...>>createTrpcClient({createTrpcClient<RouterType<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }; }>, readonly [...]>(opts: TypedClientOptions<...>): TrpcClient<...>router:TypedClientOptions<RouterType<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<...>; services: never; }>; create: Procedure<...>; }; }>, readonly [...]>.router: RouterType<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }; }>routerType<routerType<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }; }>(): RouterType<...>AppRouter>(),type AppRouter = { post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }; }links: [TypedClientOptions<RouterType<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<...>; services: never; }>; create: Procedure<...>; }; }>, readonly [...]>.links: readonly [TrpcLink<{ form: JsonForm; provides: "response"; }, any>]httpLink({httpLink<{ url: string; }>(opts: { url: string; }): TrpcLink<{ form: JsonForm; provides: "response"; }, any>url: stringurl: '/trpc' })], }); const post = awaitclient.const client: TrpcClient<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }; }, {}, undefined, LinksTypes<...>>post.post: ClientRouter<{ byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }, {}, undefined, LinksTypes<...>>byId.byId: { query(input: { id: string; }, opts?: CallOptions<{}> | undefined): TrpcPromise<{ id: string; title: string; }, CallError<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }, JsonForm, never>>; }query({function query(input: { id: string; }, opts?: CallOptions<{}> | undefined): TrpcPromise<{ id: string; title: string; }, CallError<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }, JsonForm, never>>id: stringid: '1' });var console: Consoleconsole.Console.log(...data: any[]): voidlog(post.const post: { id: string; title: string; }title: stringtitle); const [created,const created: { id: string; title: string; } | undefinederr] = awaitconst err: (TrpcError<"PARSE_ERROR", undefined> & { readonly defined: false; }) | (TrpcError<"BAD_REQUEST", undefined> & { readonly defined: false; }) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & { readonly defined: false; }) | (TrpcError<"UNAUTHORIZED", undefined> & { readonly defined: false; }) | (TrpcError<"PAYMENT_REQUIRED", undefined> & { readonly defined: false; }) | (TrpcError<"FORBIDDEN", undefined> & { readonly defined: false; }) | (TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 19 more ... | undefinedsafe(safe<{ id: string; title: string; }, (TrpcError<"PARSE_ERROR", undefined> & { readonly defined: false; }) | (TrpcError<"BAD_REQUEST", undefined> & { readonly defined: false; }) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & { readonly defined: false; }) | (TrpcError<"UNAUTHORIZED", undefined> & { readonly defined: false; }) | (TrpcError<"PAYMENT_REQUIRED", undefined> & { readonly defined: false; }) | (TrpcError<"FORBIDDEN", undefined> & { readonly defined: false; }) | ... 19 more ... | (TrpcError<...> & { ...; })>(promise: PromiseLike<...> & { ...; }): Promise<...>client.const client: TrpcClient<{ post: { byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }; }, {}, undefined, LinksTypes<...>>post.post: ClientRouter<{ byId: Procedure<{ type: "query"; ctx: { userId: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", { id: string; }> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }>; }, {}, undefined, LinksTypes<...>>create.create: { mutate(input: { title: string; }, opts?: CallOptions<{}> | undefined): TrpcPromise<{ id: string; title: string; }, CallError<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }, JsonForm, never>>; }mutate({function mutate(input: { title: string; }, opts?: CallOptions<{}> | undefined): TrpcPromise<{ id: string; title: string; }, CallError<{ type: "mutation"; ctx: { userId: string | null; }; meta: {}; input: { title: string; }; output: Post; errors: SchemaValidationError<ZodObject<{ title: ZodString; }, $strip>>; services: never; }, JsonForm, never>>title: stringtitle: '' })); if (err) {const err: (TrpcError<"PARSE_ERROR", undefined> & { readonly defined: false; }) | (TrpcError<"BAD_REQUEST", undefined> & { readonly defined: false; }) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & { readonly defined: false; }) | (TrpcError<"UNAUTHORIZED", undefined> & { readonly defined: false; }) | (TrpcError<"PAYMENT_REQUIRED", undefined> & { readonly defined: false; }) | (TrpcError<"FORBIDDEN", undefined> & { readonly defined: false; }) | (TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 19 more ... | undefinedmatchError(matchError<(TrpcError<"PARSE_ERROR", undefined> & { readonly defined: false; }) | (TrpcError<"BAD_REQUEST", undefined> & { readonly defined: false; }) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & { readonly defined: false; }) | (TrpcError<"UNAUTHORIZED", undefined> & { readonly defined: false; }) | (TrpcError<"PAYMENT_REQUIRED", undefined> & { readonly defined: false; }) | (TrpcError<"FORBIDDEN", undefined> & { readonly defined: false; }) | (TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 18 more ... | (TrpcError<...> & { ...; }), { ...; }>(err: (TrpcError<...> & { readonly defined: false; }) | ... 24 more ... | (TrpcError<...> & { ...; }), handlers: { ...; }): voiderr, {const err: (TrpcError<"PARSE_ERROR", undefined> & { readonly defined: false; }) | (TrpcError<"BAD_REQUEST", undefined> & { readonly defined: false; }) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & { readonly defined: false; }) | (TrpcError<"UNAUTHORIZED", undefined> & { readonly defined: false; }) | (TrpcError<"PAYMENT_REQUIRED", undefined> & { readonly defined: false; }) | (TrpcError<"FORBIDDEN", undefined> & { readonly defined: false; }) | (TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 18 more ... | (TrpcError<...> & { ...; })BAD_REQUEST: (type BAD_REQUEST: (e: TrpcError<"BAD_REQUEST", SchemaInputErrorData<ZodObject<{ title: ZodString; }, $strip>>> & { readonly defined: true; }) => voide) =>e: TrpcError<...> & { ...; }var console: Consoleconsole.Console.log(...data: any[]): voidlog(e.e: TrpcError<...> & { ...; }data.TrpcError<"BAD_REQUEST", SchemaInputErrorData<ZodObject<{ title: ZodString; }, $strip>>>.data: SchemaInputErrorData<ZodObject<{ title: ZodString; }, $strip>>issues),SchemaInputErrorData<ZodObject<{ title: ZodString; }, $strip>>.issues: readonly ({ readonly code: "invalid_type"; readonly expected: $ZodInvalidTypeExpected; readonly path: readonly [] | readonly ["title"]; readonly message: string; } | { readonly code: "too_big"; readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {}); readonly maximum: string | number; readonly inclusive?: boolean | undefined; readonly exact?: boolean | undefined; readonly path: readonly [] | readonly ["title"]; readonly message: string; } | { readonly code: "too_small"; readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {}); ... 4 more ...; readonly message: string; } | ... 8 more ... | { ...; })[]builtin: (builtin: (e: (TrpcError<"PARSE_ERROR", undefined> & { readonly defined: false; }) | (TrpcError<"BAD_REQUEST", undefined> & { readonly defined: false; }) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & { readonly defined: false; }) | (TrpcError<"UNAUTHORIZED", undefined> & { readonly defined: false; }) | (TrpcError<"PAYMENT_REQUIRED", undefined> & { readonly defined: false; }) | (TrpcError<"FORBIDDEN", undefined> & { readonly defined: false; }) | (TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 17 more ... | (TrpcError<...> & { readonly defined: false; })) => voide) =>e: (TrpcError<"PARSE_ERROR", undefined> & { readonly defined: false; }) | (TrpcError<"BAD_REQUEST", undefined> & { readonly defined: false; }) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & { readonly defined: false; }) | (TrpcError<"UNAUTHORIZED", undefined> & { readonly defined: false; }) | (TrpcError<"PAYMENT_REQUIRED", undefined> & { readonly defined: false; }) | (TrpcError<"FORBIDDEN", undefined> & { readonly defined: false; }) | (TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 17 more ... | (TrpcError<...> & { readonly defined: false; })var console: Consoleconsole.Console.log(...data: any[]): voidlog(e.e: (TrpcError<"PARSE_ERROR", undefined> & { readonly defined: false; }) | (TrpcError<"BAD_REQUEST", undefined> & { readonly defined: false; }) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & { readonly defined: false; }) | (TrpcError<"UNAUTHORIZED", undefined> & { readonly defined: false; }) | (TrpcError<"PAYMENT_REQUIRED", undefined> & { readonly defined: false; }) | (TrpcError<"FORBIDDEN", undefined> & { readonly defined: false; }) | (TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 17 more ... | (TrpcError<...> & { readonly defined: false; })TrpcError<TCode extends string = string, TData = unknown>.code: "PARSE_ERROR" | "BAD_REQUEST" | "UNSUPPORTED_PROTOCOL" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT" | "NETWORK_ERROR" | "BAD_RESPONSE" | "CLIENT_ERROR" | "STREAM_INTERRUPTED"code,e.e: (TrpcError<"PARSE_ERROR", undefined> & { readonly defined: false; }) | (TrpcError<"BAD_REQUEST", undefined> & { readonly defined: false; }) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & { readonly defined: false; }) | (TrpcError<"UNAUTHORIZED", undefined> & { readonly defined: false; }) | (TrpcError<"PAYMENT_REQUIRED", undefined> & { readonly defined: false; }) | (TrpcError<"FORBIDDEN", undefined> & { readonly defined: false; }) | (TrpcError<"NOT_FOUND", undefined> & { readonly defined: false; }) | ... 17 more ... | (TrpcError<...> & { readonly defined: false; })Error.message: stringmessage), }); } else {var console: Consoleconsole.Console.log(...data: any[]): voidlog(created.id: stringid);}
FeaturesWhat's in v12
- Automatic typesafetyMade a server side change? TypeScript will warn you of errors on your client before you even save the file!
- Snappy DXtRPC has no build or compile steps, meaning no code generation, runtime bloat or build step.
- Framework agnosticCompatible with all JavaScript frameworks and runtimes. It's easy to add to your existing projects.
- AutocompletionUsing tRPC is like using an SDK for your API's server code, giving you confidence in your endpoints.
- Light bundle sizetRPC has zero dependencies and a tiny client-side footprint making it lightweight.
- Batteries includedWe provide adapters for React, Next.js, Express, Fastify, AWS Lambda, Solid, Svelte, and more.
One package, @trpc12/trpc, with an entry point per job and nothing to install twice.
- Built on EffectProcedures can return an Effect, and the client can be one. Middleware, services and errors compose with the rest of your Effect app.
- Streams and filesReturn an async iterable to stream; send and receive files and blobs. Every link batches and streams by default.
- Subscriptions that resume
tracked()events resume after a reconnect, over SSE or WebSockets. - Every runtimeNode, Bun, Deno, Cloudflare Workers, AWS Lambda, Express and Fastify.
- Framework integrationsQuery, mutation and subscription options for TanStack Query in React; Server Actions and RSC helpers.
- Migrate one step at a time
createV11Handlerserves v11 clients from a v12 router while you migrate, and a codemod rewrites most of the code.
Sponsors
tRPC is MIT licensed and built in the open, kept going by the companies and people below. Thank you!
Partners
