Getting started
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.
pnpm add @trpc12/trpc zodnpm install @trpc12/trpc zodyarn add @trpc12/trpc zodbun add @trpc12/trpc zodDefine 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: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}
appRouter = {
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
}
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: 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>): BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: 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: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: 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): BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: 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: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}
AppRouter = typeof const appRouter: {
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}
appRouter;
export const const handler: FetchHandler<undefined>handler = createFetchHandler<{
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}, undefined>(opts: FetchHandlerOptions<...>): FetchHandler<...>
createFetchHandler({
BaseHandlerOptions<{ post: { byId: BuildProcedure<InputDef<RootDef<{ user: string | null; }, {}>, ZodObject<{ id: ZodString; }, $strip>>, "query", { output: Post; errors: TrpcError<...>; services: never; unmapped: never; }>; create: BuildProcedure<...>; }; }>.router: {
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}
router: const appRouter: {
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: 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 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: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}
AppRouter } from './server.ts';
const const client: TrpcClient<{
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}, {}, undefined, LinksTypes<...>>
client = createTrpcClient<RouterType<{
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}>, readonly [...]>(opts: TypedClientOptions<...>): TrpcClient<...>
createTrpcClient({
TypedClientOptions<RouterType<{ post: { byId: BuildProcedure<InputDef<RootDef<{ user: string | null; }, {}>, ZodObject<{ id: ZodString; }, $strip>>, "query", { ...; }>; create: BuildProcedure<...>; }; }>, readonly [...]>.router: RouterType<{
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}>
router: routerType<{
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}>(): RouterType<...>
routerType<type AppRouter = {
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}
AppRouter>(),
TypedClientOptions<RouterType<{ post: { byId: BuildProcedure<InputDef<RootDef<{ user: string | null; }, {}>, ZodObject<{ id: ZodString; }, $strip>>, "query", { ...; }>; create: BuildProcedure<...>; }; }>, 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: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}, {}, undefined, LinksTypes<...>>
client.post: ClientRouter<{
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: 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> | InputValidationError<{
readonly code: "invalid_type";
readonly expected: $ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["id"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>;
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> | InputValidationError<{
readonly code: "invalid_type";
readonly expected: $ZodInvalidTypeExpected;
readonly path: readonly [] | readonly ["id"];
readonly message: string;
} | {
readonly code: "too_big";
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>;
services: never;
}, JsonForm, never>>
query({ id: stringid: '1' });const [const created: {
id: string;
title: string;
author: string | null;
createdAt: string;
} | undefined
created, const err: UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
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;
} | {
...;
} | ... 8 more ... | {
...;
}>> & {
...;
}) | undefined
err] = await safe<{
id: string;
title: string;
author: string | null;
createdAt: string;
}, UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
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 ... | {
...;
}>> & {
...;
})>(promise: PromiseLike<...> & {
...;
}): Promise<...>
safe(const client: TrpcClient<{
post: {
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: never;
}>;
};
}, {}, undefined, LinksTypes<...>>
client.post: ClientRouter<{
byId: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
id: ZodString;
}, $strip>>, "query", {
output: Post;
errors: TrpcError<"NOT_FOUND", undefined>;
services: never;
unmapped: never;
}>;
create: BuildProcedure<InputDef<RootDef<{
user: string | null;
}, {}>, ZodObject<{
title: ZodString;
}, $strip>>, "mutation", {
output: Post;
errors: never;
services: never;
unmapped: 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: InputValidationError<{
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 & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>;
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: InputValidationError<{
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 & {});
... 4 more ...;
readonly message: string;
} | ... 9 more ... | {
...;
}>;
services: never;
}, JsonForm, never>>
mutate({ title: stringtitle: '' }));
if (const err: UnexpectedError | (TrpcError<"BAD_REQUEST", InputValidationErrorData<{
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;
} | {
...;
} | ... 8 more ... | {
...;
}>> & {
...;
}) | undefined
err?.defined: boolean | undefineddefined && const err: TrpcError<"BAD_REQUEST", InputValidationErrorData<{
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;
} | {
...;
} | ... 8 more ... | {
...;
}>> & {
...;
}
err.TrpcError<"BAD_REQUEST", InputValidationErrorData<{ readonly code: "invalid_type"; readonly expected: $ZodInvalidTypeExpected; readonly path: readonly [] | readonly ["title"]; readonly message: string; } | ... 10 more ... | { ...; }>>.code: "BAD_REQUEST"code === 'BAD_REQUEST') {
const err: TrpcError<"BAD_REQUEST", InputValidationErrorData<{
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;
} | {
...;
} | ... 8 more ... | {
...;
}>> & {
...;
}
err.TrpcError<"BAD_REQUEST", InputValidationErrorData<{ readonly code: "invalid_type"; readonly expected: $ZodInvalidTypeExpected; readonly path: readonly [] | readonly ["title"]; readonly message: string; } | ... 10 more ... | { ...; }>>.data: InputValidationErrorData<{
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;
} | {
...;
} | ... 8 more ... | {
...;
}>
data.issues;}routerType<AppRouter>() types the client from the router type alone, and the client also types what its links read (see The client).
Naming
Names case acronyms as words: TrpcError, initTrpc, createTrpcClient, OpenApi…, Json….
Next steps
- Procedures: input, output and the three procedure types.
- Errors: typed errors without declaring them.
- Middleware: context, base procedures and reusable middleware.
- Migrating from v11: serve old clients while you move.