Skip to content

tRPC v12 isn’t on npm yet. The @trpc12/* packages on this site can’t be installed until it’s released. Use v11 today

tRPC

Getting started

A router, a handler that serves it, and a client that calls it with the router's types.

On this page

A router is a plain object of procedures. A handler serves it; a client calls it with the router's type.

Not on npm yet
pnpm add @trpc12/trpc effect
npm install @trpc12/trpc effect
yarn add @trpc12/trpc effect
bun add @trpc12/trpc effect

effect is a peer dependency, so your app and @trpc12/trpc share one copy; code that uses Promises never imports it (Packages).

.input() takes any Standard Schema (zod, Valibot, ArkType, …) or an Effect Schema (Input and output). This page uses zod:

pnpm add zod
npm install zod
yarn add zod
bun add zod

Define a router

server.ts
import { function createFetchHandler<TRouter extends AnyRouter, TPlatform = undefined>(opts: FetchHandlerOptions<TRouter, TPlatform>): FetchHandler<TPlatform>createFetchHandler, function error<const TCode extends string, TData = undefined>(opts: Omit<TrpcErrorOptions<TCode, TData>, "defined">): TrpcError<TCode, TData>error, function initTrpc<TConfig extends RootConfig = {}>(opts?: RootOptions<TConfig["meta"] extends object ? TConfig["meta"] : {}>): TrpcRoot<TConfig["ctx"] extends object ? TConfig["ctx"] : {}, TConfig["meta"] extends object ? TConfig["meta"] : {}>initTrpc } from '@trpc12/trpc/server';
import { import zz } from 'zod';

const 
const t: TrpcRoot<{
    user: string | null;
}, {}, RootDef<{
    user: string | null;
}, {}>>
t
=
initTrpc<{
    ctx: {
        user: string | null;
    };
}>(opts?: RootOptions<{}>): TrpcRoot<{
    user: string | null;
}, {}, RootDef<{
    user: string | null;
}, {}>>
initTrpc
<{
ctx: {
    user: string | null;
}
ctx
: { user: string | nulluser: string | null } }>();
export const
const appRouter: {
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<z.ZodObject<{
                id: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                title: string;
            };
            output: Post;
            errors: SchemaValidationError<z.ZodObject<{
                title: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
    };
}
appRouter
= {
post: {
    byId: Procedure<{
        type: "query";
        ctx: {
            user: string | null;
        };
        meta: {};
        input: {
            id: string;
        };
        output: Post;
        errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<z.ZodObject<{
            id: z.ZodString;
        }, z.core.$strip>>;
        services: never;
    }>;
    create: Procedure<{
        type: "mutation";
        ctx: {
            user: string | null;
        };
        meta: {};
        input: {
            title: string;
        };
        output: Post;
        errors: SchemaValidationError<z.ZodObject<{
            title: z.ZodString;
        }, z.core.$strip>>;
        services: never;
    }>;
}
post
: {
byId: Procedure<{
    type: "query";
    ctx: {
        user: string | null;
    };
    meta: {};
    input: {
        id: string;
    };
    output: Post;
    errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<z.ZodObject<{
        id: z.ZodString;
    }, z.core.$strip>>;
    services: never;
}>
byId
:
const t: TrpcRoot<{
    user: string | null;
}, {}, RootDef<{
    user: string | null;
}, {}>>
t
.
TrpcRoot<{ user: string | null; }, {}, RootDef<{ user: string | null; }, {}>>.procedure: ProcedureBuilder<RootDef<{
    user: string | null;
}, {}>>
procedure
.
ProcedureBuilder<RootDef<{ user: string | null; }, {}>>.input<z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<InputDef<RootDef<{
    user: string | null;
}, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>> (+1 overload)
input
(import zz.
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>
object
({ id: z.ZodStringid: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }))
.
ProcedureBuilder<InputDef<RootDef<{ user: string | null; }, {}>, ZodObject<{ id: ZodString; }, $strip>>>.query<Post | TrpcError<"NOT_FOUND", undefined>>(resolver: (opts: ResolverOpts<InputDef<RootDef<{
    user: string | null;
}, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>) => Post | TrpcError<"NOT_FOUND", undefined>): Procedure<{
    type: "query";
    ctx: {
        user: string | null;
    };
    meta: {};
    input: {
        id: string;
    };
    output: Post;
    errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<z.ZodObject<{
        id: z.ZodString;
    }, z.core.$strip>>;
    services: never;
}>
query
(
(
opts: ResolverOpts<InputDef<RootDef<{
    user: string | null;
}, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>
opts
) =>
const db: {
    post: {
        find: (id: string) => Post | undefined;
        list: () => Post[];
        create: (input: {
            title: string;
            author?: string | null;
        }) => Post;
    };
}
db
.
post: {
    find: (id: string) => Post | undefined;
    list: () => Post[];
    create: (input: {
        title: string;
        author?: string | null;
    }) => Post;
}
post
.find: (id: string) => Post | undefinedfind(
opts: ResolverOpts<InputDef<RootDef<{
    user: string | null;
}, {}>, z.ZodObject<{
    id: z.ZodString;
}, z.core.$strip>>>
opts
.
ResolverOpts<InputDef<RootDef<{ user: string | null; }, {}>, ZodObject<{ id: ZodString; }, $strip>>>.input: {
    id: string;
}
input
.id: stringid) ?? error<"NOT_FOUND", undefined>(opts: Omit<TrpcErrorOptions<"NOT_FOUND", undefined>, "defined">): TrpcError<"NOT_FOUND", undefined>error({ code: "NOT_FOUND"code: 'NOT_FOUND' }),
),
create: Procedure<{
    type: "mutation";
    ctx: {
        user: string | null;
    };
    meta: {};
    input: {
        title: string;
    };
    output: Post;
    errors: SchemaValidationError<z.ZodObject<{
        title: z.ZodString;
    }, z.core.$strip>>;
    services: never;
}>
create
:
const t: TrpcRoot<{
    user: string | null;
}, {}, RootDef<{
    user: string | null;
}, {}>>
t
.
TrpcRoot<{ user: string | null; }, {}, RootDef<{ user: string | null; }, {}>>.procedure: ProcedureBuilder<RootDef<{
    user: string | null;
}, {}>>
procedure
.
ProcedureBuilder<RootDef<{ user: string | null; }, {}>>.input<z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<InputDef<RootDef<{
    user: string | null;
}, {}>, z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>> (+1 overload)
input
(import zz.
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>
object
({ 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) }))
.
ProcedureBuilder<InputDef<RootDef<{ user: string | null; }, {}>, ZodObject<{ title: ZodString; }, $strip>>>.mutation<Post>(resolver: (opts: ResolverOpts<InputDef<RootDef<{
    user: string | null;
}, {}>, z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>>) => Post): Procedure<{
    type: "mutation";
    ctx: {
        user: string | null;
    };
    meta: {};
    input: {
        title: string;
    };
    output: Post;
    errors: SchemaValidationError<z.ZodObject<{
        title: z.ZodString;
    }, z.core.$strip>>;
    services: never;
}>
mutation
((
opts: ResolverOpts<InputDef<RootDef<{
    user: string | null;
}, {}>, z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>>
opts
) =>
const db: {
    post: {
        find: (id: string) => Post | undefined;
        list: () => Post[];
        create: (input: {
            title: string;
            author?: string | null;
        }) => Post;
    };
}
db
.
post: {
    find: (id: string) => Post | undefined;
    list: () => Post[];
    create: (input: {
        title: string;
        author?: string | null;
    }) => Post;
}
post
.
create: (input: {
    title: string;
    author?: string | null;
}) => Post
create
({ ...
opts: ResolverOpts<InputDef<RootDef<{
    user: string | null;
}, {}>, z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>>
opts
.
ResolverOpts<InputDef<RootDef<{ user: string | null; }, {}>, ZodObject<{ title: ZodString; }, $strip>>>.input: {
    title: string;
}
input
, author?: string | null | undefinedauthor:
opts: ResolverOpts<InputDef<RootDef<{
    user: string | null;
}, {}>, z.ZodObject<{
    title: z.ZodString;
}, z.core.$strip>>>
opts
.
ResolverOpts<InputDef<RootDef<{ user: string | null; }, {}>, ZodObject<{ title: ZodString; }, $strip>>>.ctx: {
    user: string | null;
}
ctx
.user: string | nulluser }),
), }, }; export type
type AppRouter = {
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<z.ZodObject<{
                id: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                title: string;
            };
            output: Post;
            errors: SchemaValidationError<z.ZodObject<{
                title: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
    };
}
AppRouter
= typeof
const appRouter: {
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<z.ZodObject<{
                id: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                title: string;
            };
            output: Post;
            errors: SchemaValidationError<z.ZodObject<{
                title: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
    };
}
appRouter
;
export const const handler: FetchHandler<undefined>handler =
createFetchHandler<{
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<z.ZodObject<{
                id: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                title: string;
            };
            output: Post;
            errors: SchemaValidationError<z.ZodObject<{
                title: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
    };
}, undefined>(opts: FetchHandlerOptions<...>): FetchHandler<...>
createFetchHandler
({
BaseHandlerOptions<{ post: { byId: Procedure<{ type: "query"; ctx: { user: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; create: Procedure<...>; }; }>.router: {
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<z.ZodObject<{
                id: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                title: string;
            };
            output: Post;
            errors: SchemaValidationError<z.ZodObject<{
                title: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
    };
}
router
:
const appRouter: {
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<z.ZodObject<{
                id: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                title: string;
            };
            output: Post;
            errors: SchemaValidationError<z.ZodObject<{
                title: z.ZodString;
            }, z.core.$strip>>;
            services: never;
        }>;
    };
}
appRouter
,
createContext: (opts: CreateContextOpts<undefined>) => MaybePromise<{
    user: string | null;
}>
createContext
: (opts: CreateContextOpts<undefined>opts) => ({
user: string | nulluser: opts: CreateContextOpts<undefined>opts.CreateContextOpts<undefined>.request: HttpServerRequestrequest.HttpIncomingMessage<HttpServerError>.headers: Headersheaders['authorization'] ?? null, }), });

request is Effect's HttpServerRequest: headers is a record of lowercase names, and on fetch runtimes request.source is the web Request.

Serve it

handler.fetch(request) is a web-standard handler: see Adapters to serve it. On Node:

main.ts
import { function createServer<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse = typeof ServerResponse>(requestListener?: RequestListener<Request, Response>): Server<Request, Response> (+1 overload)createServer } from 'node:http';
import { import NodeHttpServerNodeHttpServer, import NodeRuntimeNodeRuntime } from '@effect/platform-node';
import { import LayerLayer } from 'effect';
import { function serve(handler: FetchLike | (FetchLike & WebSocketHandlerLike), opts?: HttpOptions): Layer.Layer<never, never, HttpServer>serve } from '@trpc12/trpc/http';

function serve(handler: FetchLike | (FetchLike & WebSocketHandlerLike), opts?: HttpOptions): Layer.Layer<never, never, HttpServer>serve(const handler: FetchHandler<undefined>handler).Pipeable.pipe<Layer.Layer<never, never, HttpServer>, Layer.Layer<never, ServeError, never>, Effect<never, ServeError, never>, void>(this: Layer.Layer<never, never, HttpServer>, ab: (_: Layer.Layer<never, never, HttpServer>) => Layer.Layer<never, ServeError, never>, bc: (_: Layer.Layer<never, ServeError, never>) => Effect<never, ServeError, never>, cd: (_: Effect<never, ServeError, never>) => void): void (+21 overloads)pipe(
  import LayerLayer.const provide: <never, ServeError, HttpServer | NodeServices | HttpPlatform | Generator>(that: Layer.Layer<HttpServer | NodeServices | HttpPlatform | Generator, ServeError, never>) => <RIn2, E2, ROut2>(self: Layer.Layer<ROut2, E2, RIn2>) => Layer.Layer<ROut2, ServeError | E2, Exclude<RIn2, HttpServer | NodeServices | HttpPlatform | Generator>> (+3 overloads)provide(import NodeHttpServerNodeHttpServer.const layer: (evaluate: LazyArg<Server>, options: NodeHttpServer.Options) => Layer.Layer<HttpServer | NodeServices | HttpPlatform | Generator, ServeError>layer(function createServer<Request extends typeof IncomingMessage = typeof IncomingMessage, Response extends typeof ServerResponse = typeof ServerResponse>(requestListener?: RequestListener<Request, Response>): Server<Request, Response> (+1 overload)createServer, { ListenOptions.port?: number | undefinedport: 3000 })),
  import LayerLayer.const launch: <RIn, E, ROut>(self: Layer.Layer<ROut, E, RIn>) => Effect<never, E, RIn>launch,
  import NodeRuntimeNodeRuntime.
const runMain: {
    (options?: {
        readonly disableErrorReporting?: boolean | undefined;
        readonly teardown?: Teardown | undefined;
    }): <E, A>(effect: Effect<A, E>) => void;
    <E, A>(effect: Effect<A, E>, options?: {
        readonly disableErrorReporting?: boolean | undefined;
        readonly teardown?: Teardown | undefined;
    }): void;
}
runMain
,
);

Call it

The client imports only the type:

client.ts
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 matchError<E extends AnyTrpcError, H extends ErrorHandlers<E> & { readonly [K in keyof H]: K extends keyof ErrorHandlers<E> ? unknown : never; }>(err: E, handlers: H): HandlerResult<H>matchError,
  function routerType<TRouter>(): RouterType<TRouter>routerType,
  
function safe<T, E = unknown>(promise: PromiseLike<T> & {
    readonly "~error"?: E;
}): Promise<SafeResult<T, E>>
safe
,
} from '@trpc12/trpc/client'; import type {
type AppRouter = {
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
                id: ZodString;
            }, $strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: 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: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
                id: ZodString;
            }, $strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: 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: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
                id: ZodString;
            }, $strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: 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: { user: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{ ...; }, $strip>>; services: never; }>; create: Procedure<...>; }; }>, readonly [...]>.router: RouterType<{
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
                id: ZodString;
            }, $strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                title: string;
            };
            output: Post;
            errors: SchemaValidationError<ZodObject<{
                title: ZodString;
            }, $strip>>;
            services: never;
        }>;
    };
}>
router
:
routerType<{
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
                id: ZodString;
            }, $strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: 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: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
                id: ZodString;
            }, $strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: 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: { user: string | null; }; meta: {}; input: { id: string; }; output: Post; errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{ ...; }, $strip>>; 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: 'http://localhost:3000/trpc' })],
}); const post = await
const client: TrpcClient<{
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
                id: ZodString;
            }, $strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: 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: {
            user: string | null;
        };
        meta: {};
        input: {
            id: string;
        };
        output: Post;
        errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
            id: ZodString;
        }, $strip>>;
        services: never;
    }>;
    create: Procedure<{
        type: "mutation";
        ctx: {
            user: 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;
        author: string | null;
        createdAt: string;
    }, CallError<{
        type: "query";
        ctx: {
            user: string | null;
        };
        meta: {};
        input: {
            id: string;
        };
        output: Post;
        errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
            id: ZodString;
        }, $strip>>;
        services: never;
    }, JsonForm, never>>;
}
byId
.
function query(input: {
    id: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
    id: string;
    title: string;
    author: string | null;
    createdAt: string;
}, CallError<{
    type: "query";
    ctx: {
        user: string | null;
    };
    meta: {};
    input: {
        id: string;
    };
    output: Post;
    errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
        id: ZodString;
    }, $strip>>;
    services: never;
}, JsonForm, never>>
query
({ id: stringid: '1' });
const post: {
    id: string;
    title: string;
    author: string | null;
    createdAt: string;
}
const [
const created: {
    id: string;
    title: string;
    author: string | null;
    createdAt: string;
} | undefined
created
,
const err: (TrpcError<"NOT_FOUND", undefined> & {
    readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
    readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", 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 ... | undefined
err
] = await
safe<{
    id: string;
    title: string;
    author: string | null;
    createdAt: string;
}, (TrpcError<"NOT_FOUND", undefined> & {
    readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
    readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", undefined> & {
    readonly defined: false;
}) | (TrpcError<"UNSUPPORTED_PROTOCOL", undefined> & {
    readonly defined: false;
}) | (TrpcError<"UNAUTHORIZED", undefined> & {
    readonly defined: false;
}) | (TrpcError<"PAYMENT_REQUIRED", undefined> & {
    readonly defined: false;
}) | ... 19 more ... | (TrpcError<...> & {
    ...;
})>(promise: PromiseLike<...> & {
    ...;
}): Promise<...>
safe
(
const client: TrpcClient<{
    post: {
        byId: Procedure<{
            type: "query";
            ctx: {
                user: string | null;
            };
            meta: {};
            input: {
                id: string;
            };
            output: Post;
            errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
                id: ZodString;
            }, $strip>>;
            services: never;
        }>;
        create: Procedure<{
            type: "mutation";
            ctx: {
                user: 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: {
            user: string | null;
        };
        meta: {};
        input: {
            id: string;
        };
        output: Post;
        errors: TrpcError<"NOT_FOUND", undefined> | SchemaValidationError<ZodObject<{
            id: ZodString;
        }, $strip>>;
        services: never;
    }>;
    create: Procedure<{
        type: "mutation";
        ctx: {
            user: string | null;
        };
        meta: {};
        input: {
            title: string;
        };
        output: Post;
        errors: SchemaValidationError<ZodObject<{
            title: ZodString;
        }, $strip>>;
        services: never;
    }>;
}, {}, undefined, LinksTypes<...>>
post
.
create: {
    mutate(input: {
        title: string;
    }, opts?: CallOptions<{}> | undefined): TrpcPromise<{
        id: string;
        title: string;
        author: string | null;
        createdAt: string;
    }, CallError<{
        type: "mutation";
        ctx: {
            user: string | null;
        };
        meta: {};
        input: {
            title: string;
        };
        output: Post;
        errors: SchemaValidationError<ZodObject<{
            title: ZodString;
        }, $strip>>;
        services: never;
    }, JsonForm, never>>;
}
create
.
function mutate(input: {
    title: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
    id: string;
    title: string;
    author: string | null;
    createdAt: string;
}, CallError<{
    type: "mutation";
    ctx: {
        user: string | null;
    };
    meta: {};
    input: {
        title: string;
    };
    output: Post;
    errors: SchemaValidationError<ZodObject<{
        title: ZodString;
    }, $strip>>;
    services: never;
}, JsonForm, never>>
mutate
({ title: stringtitle: '' }));
if (
const err: (TrpcError<"NOT_FOUND", undefined> & {
    readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
    readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", 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 ... | undefined
err
) {
matchError<(TrpcError<"NOT_FOUND", undefined> & {
    readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
    readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", 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;
}) | ... 18 more ... | (TrpcError<...> & {
    ...;
}), {
    ...;
}>(err: (TrpcError<...> & {
    readonly defined: false;
}) | ... 24 more ... | (TrpcError<...> & {
    ...;
}), handlers: {
    ...;
}): HandlerResult<...>
matchError
(
const err: (TrpcError<"NOT_FOUND", undefined> & {
    readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
    readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", 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;
}) | ... 18 more ... | (TrpcError<...> & {
    ...;
})
err
, {
type BAD_REQUEST: (e: TrpcError<"BAD_REQUEST", SchemaInputErrorData<ZodObject<{
    title: ZodString;
}, $strip>>> & {
    readonly defined: true;
}) => 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 [...];
    readonly message: string;
} | ... 9 more ... | {
    ...;
})[]
BAD_REQUEST
: (
e: TrpcError<...> & {
    ...;
}
e
) =>
e: TrpcError<...> & {
    ...;
}
e
.
TrpcError<"BAD_REQUEST", SchemaInputErrorData<ZodObject<{ title: ZodString; }, $strip>>>.data: SchemaInputErrorData<ZodObject<{
    title: ZodString;
}, $strip>>
data
.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: (e: (TrpcError<"NOT_FOUND", undefined> & {
    readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
    readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", 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;
}) | ... 17 more ... | (TrpcError<...> & {
    readonly defined: false;
})) => string
builtin
: (
e: (TrpcError<"NOT_FOUND", undefined> & {
    readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
    readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", 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;
}) | ... 17 more ... | (TrpcError<...> & {
    readonly defined: false;
})
e
) =>
e: (TrpcError<"NOT_FOUND", undefined> & {
    readonly defined: false;
}) | (TrpcError<"BAD_REQUEST", undefined> & {
    readonly defined: false;
}) | (TrpcError<"PARSE_ERROR", 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;
}) | ... 17 more ... | (TrpcError<...> & {
    readonly defined: false;
})
e
.Error.message: stringmessage,
}); } else {
const created: {
    id: string;
    title: string;
    author: string | null;
    createdAt: string;
}
created
.id: stringid;
}

routerType<AppRouter>() types the client from the router type alone, and the client also types what its links read (see The client).

safe() resolves to [data, error] instead of throwing. Once error is handled, data is the output. matchError() takes a handler for each of the procedure's errors and one for the built-in ones (network failures, aborts, crashes), and leaving one out is a type error (Errors without try/catch).

Naming

Names case acronyms as words: TrpcError, initTrpc, createTrpcClient, OpenApi…, Json….

Next steps