Server Side Calls
On this page
You may need to call your procedure(s) directly from the same server they're hosted in, createCallerFactory() can be used to achieve this. This is useful for server-side calls and for integration testing of your tRPC procedures.
Create caller
With the t.createCallerFactory-function you can create a server-side caller of any router. You first call createCallerFactory with an argument of the router you want to call, then this returns a function where you can pass in a Context for the following procedure calls.
Basic example
We create the router with a query to list posts and a mutation to add posts, and then we call each method.
import { const initTRPC: TRPCBuilder<object, object>initTRPC } from '@trpc/server';
import { import zz } from 'zod';
type type Context = {
foo: string;
}
Context = {
foo: stringfoo: string;
};
const const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<object, object>.context<Context>(): TRPCBuilder<Context, object>context<type Context = {
foo: string;
}
Context>().TRPCBuilder<Context, object>.create<RuntimeConfigOptions<Context, object>>(opts?: RuntimeConfigOptions<Context, object> | undefined): TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
create();
const const publicProcedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>publicProcedure = const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>procedure;
const { const createCallerFactory: RouterCallerFactory<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
createCallerFactory, const router: RouterBuilder<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
router } = const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t;
interface Post {
Post.id: stringid: string;
Post.title: stringtitle: string;
}
const const posts: Post[]posts: Post[] = [
{
Post.id: stringid: '1',
Post.title: stringtitle: 'Hello world',
},
];
const const appRouter: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
appRouter = const router: RouterBuilder
<{
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>(_: {
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}) => BuiltRouter<...>
router({
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post: const router: RouterBuilder
<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>(_: {
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}) => BuiltRouter<...>
router({
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>
add: const publicProcedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>publicProcedure
.ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.input<z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
title: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<Context, object, object, {
title: string;
}, {
title: string;
}, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
input(
import zz.function object<{
title: z.ZodString;
}>(shape?: {
title: 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<{
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(2),
}),
)
.ProcedureBuilder<Context, object, object, { title: string; }, { title: string; }, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.mutation<Post>(resolver: ProcedureResolver<Context, object, object, {
title: string;
}, UnsetMarker, Post>): MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>
mutation((opts: ProcedureResolverOptions<Context, object, object, {
title: string;
}>
opts) => {
const const post: Postpost: Post = {
...opts: ProcedureResolverOptions<Context, object, object, {
title: string;
}>
opts.ProcedureResolverOptions<Context, object, object, { title: string; }>.input: {
title: string;
}
input,
Post.id: stringid: `${var Math: MathMath.Math.random(): numberrandom()}`,
};
const posts: Post[]posts.Array<Post>.push(...items: Post[]): numberpush(const post: Postpost);
return const post: Postpost;
}),
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>
list: const publicProcedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>publicProcedure.ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<Post[]>(resolver: ProcedureResolver<Context, object, object, UnsetMarker, UnsetMarker, Post[]>): QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>
query(() => const posts: Post[]posts),
}),
});
// 1. create a caller-function for your router
const const createCaller: RouterCaller<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
createCaller = const createCallerFactory: RouterCallerFactory
<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>(router: Pick<...>) => RouterCaller<...>
createCallerFactory(const appRouter: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
appRouter);
// 2. create a caller using your `Context`
const const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller = const createCaller: (ctx: Context | (() => MaybePromise<Context>), options?: {
onError?: RouterCallerErrorHandler<Context> | undefined;
signal?: AbortSignal;
} | undefined) => DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
createCaller({
foo: stringfoo: 'bar',
});
// 3. use the caller to add and list posts
const const addedPost: PostaddedPost = await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller.post: DecorateRouterRecord<DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.add: (input: {
title: string;
}) => Promise<Post>
add({
title: stringtitle: 'How to make server-side call in tRPC',
});
const postList = await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller.post: DecorateRouterRecord<DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
};
output: Post;
meta: object;
errorShape: DefaultErrorShape;
}>;
list: QueryProcedure<{
input: void;
output: Post[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.list: (input: void) => Promise<Post[]>list();The caller doesn't run your adapter's createContext, so pass everything your procedures and middlewares expect. An inner context works well here, as it doesn't need a request.
Instead of a context object, you can also pass a function that returns the context, or a promise of it. The caller calls it for every procedure call, so cache it (e.g. with React's cache()) if it's expensive:
const const createCaller: RouterCaller<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
createCaller = const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.createCallerFactory: RouterCallerFactory
<DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>(router: Pick<Router<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>, "_def">) => RouterCaller<...>
createCallerFactory(const appRouter: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
appRouter);
const const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
caller = const createCaller: (ctx: Context | (() => MaybePromise<Context>), options?: {
onError?: RouterCallerErrorHandler<Context> | undefined;
signal?: AbortSignal;
} | undefined) => DecorateRouterRecord<DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
createCaller(async () => function createContext(): Promise<Context>createContext());
const const greeting: stringgreeting = await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: void;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
caller.hello: (input: void) => Promise<string>hello();Example usage in an integration test
Taken from https://github.com/trpc/examples-next-prisma-starter/blob/main/src/server/routers/post.test.ts
async function function testAddAndGetPost(): Promise<void>testAddAndGetPost() {
const const ctx: {
user: undefined;
}
ctx = await function createContextInner(opts: {}): Promise<{
user: undefined;
}>
createContextInner({});
const const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: {
user: undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
text: string;
};
output: {
title: string;
text: string;
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
text: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller = function createCaller(ctx: {
user: undefined;
} | (() => MaybePromise<{
user: undefined;
}>), options?: {
onError?: RouterCallerErrorHandler<{
user: undefined;
}> | undefined;
signal?: AbortSignal;
} | undefined): DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: {
user: undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
text: string;
};
output: {
title: string;
text: string;
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
byId: QueryProcedure<{
...;
}>;
}>>;
}>>
createCaller(const ctx: {
user: undefined;
}
ctx);
const const input: {
title: string;
text: string;
}
input: type inferProcedureInput<TProcedure extends AnyProcedure> = undefined extends inferProcedureParams<TProcedure>["$types"]["input"] ? void | inferProcedureParams<TProcedure>["$types"]["input"] : inferProcedureParams<TProcedure>["$types"]["input"]inferProcedureInput<type AppRouter = Router<{
ctx: {
user: undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: {
user: undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
text: string;
};
output: {
title: string;
text: string;
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
text: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>> & DecorateCreateRouterOptions<...>
AppRouter['post']['add']> = {
text: stringtext: 'hello test',
title: stringtitle: 'hello test',
};
const const post: {
title: string;
text: string;
id: string;
}
post = await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: {
user: undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
text: string;
};
output: {
title: string;
text: string;
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
text: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller.post: DecorateRouterRecord<DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
text: string;
};
output: {
title: string;
text: string;
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
text: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.add: (input: {
title: string;
text: string;
}) => Promise<{
title: string;
text: string;
id: string;
}>
add(const input: {
title: string;
text: string;
}
input);
const const byId: {
id: string;
title: string;
text: string;
}
byId = await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: {
user: undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
text: string;
};
output: {
title: string;
text: string;
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
text: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller.post: DecorateRouterRecord<DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: {
title: string;
text: string;
};
output: {
title: string;
text: string;
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
text: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.byId: (input: {
id: string;
}) => Promise<{
id: string;
title: string;
text: string;
}>
byId({ id: stringid: const post: {
title: string;
text: string;
id: string;
}
post.id: stringid });
}router.createCaller()
With the router.createCaller({}) function (first argument is Context) we retrieve an instance of RouterCaller.
Input query example
We create the router with an input query, and then we call the asynchronous greeting procedure to get the result.
import { const initTRPC: TRPCBuilder<object, object>initTRPC } from '@trpc/server';
import { import zz } from 'zod';
const const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<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;
}>
create();
const const router: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router = const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder
<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>(_: {
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}) => BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router({
// Create procedure at path 'greeting'
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>
greeting: const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.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
.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>
input(import zz.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>
object({ name: z.ZodStringname: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }))
.ProcedureBuilder<object, object, object, { name: string; }, { name: string; }, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<string>(resolver: ProcedureResolver<object, object, object, {
name: string;
}, UnsetMarker, string>): QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>
query((opts: ProcedureResolverOptions<object, object, object, {
name: string;
}>
opts) => `Hello ${opts: ProcedureResolverOptions<object, object, object, {
name: string;
}>
opts.ProcedureResolverOptions<object, object, object, { name: string; }>.input: {
name: string;
}
input.name: stringname}`),
});
const const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
caller = const router: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router.Router<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ greeting: QueryProcedure<{ input: { name: string; }; output: string; meta: object; errorShape: DefaultErrorShape; }>; }>>.createCaller: (ctx: object | (() => MaybePromise<object>), options?: {
onError?: RouterCallerErrorHandler<object> | undefined;
signal?: AbortSignal;
} | undefined) => DecorateRouterRecord<DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
createCaller({});
const result = await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
caller.greeting: (input: {
name: string;
}) => Promise<string>
greeting({ name: stringname: 'tRPC' });Mutation example
We create the router with a mutation, and then we call the asynchronous post procedure to get the result.
import { const initTRPC: TRPCBuilder<object, object>initTRPC } from '@trpc/server';
import { import zz } from 'zod';
const const posts: string[]posts = ['One', 'Two', 'Three'];
const const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<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;
}>
create();
const const router: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
router = const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder
<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>(_: {
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}) => BuiltRouter<...>
router({
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post: const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, { ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder
<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>(_: {
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}) => BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router({
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>
add: const t: TRPCRootObject<object, object, RuntimeConfigOptions<object, object>, {
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.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.ProcedureBuilder<object, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.input<z.ZodString>(schema: z.ZodString): ProcedureBuilder<object, object, object, string, string, UnsetMarker, UnsetMarker, false, DefaultErrorShape>input(import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string()).ProcedureBuilder<object, object, object, string, string, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.mutation<string[]>(resolver: ProcedureResolver<object, object, object, string, UnsetMarker, string[]>): MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>
mutation((opts: ProcedureResolverOptions<object, object, object, string>opts) => {
const posts: string[]posts.Array<string>.push(...items: string[]): numberpush(opts: ProcedureResolverOptions<object, object, object, string>opts.ProcedureResolverOptions<object, object, object, string>.input: stringinput);
return const posts: string[]posts;
}),
}),
});
const const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller = const router: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
router.Router<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ post: BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<...>>; }>>.createCaller: (ctx: object | (() => MaybePromise<object>), options?: {
onError?: RouterCallerErrorHandler<object> | undefined;
signal?: AbortSignal;
} | undefined) => DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
createCaller({});
const result = await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller.post: DecorateRouterRecord<DecorateCreateRouterOptions<{
add: MutationProcedure<{
input: string;
output: string[];
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.add: (input: string) => Promise<string[]>add('Four');Context with middleware example
We create a middleware to check the context before executing the secret procedure. Below are two examples: the former fails because the context doesn't fit the middleware logic, and the latter works correctly.
import { const initTRPC: TRPCBuilder<object, object>initTRPC, class TRPCErrorTRPCError } from '@trpc/server';
type type Context = {
user?: {
id: string;
};
}
Context = {
user?: {
id: string;
} | undefined
user?: {
id: stringid: string;
};
};
const const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<object, object>.context<Context>(): TRPCBuilder<Context, object>context<type Context = {
user?: {
id: string;
};
}
Context>().TRPCBuilder<Context, object>.create<RuntimeConfigOptions<Context, object>>(opts?: RuntimeConfigOptions<Context, object> | undefined): TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
create();
const const protectedProcedure: ProcedureBuilder<Context, object, {
user: {
id: string;
};
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
protectedProcedure = const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>procedure.ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.use<{
user: {
id: string;
};
}>(fn: MiddlewareBuilder<{
user: {
id: string;
} | undefined;
}, object, {
user: {
id: string;
};
}, UnsetMarker> | MiddlewareFunction<Context, object, object, {
user: {
id: string;
};
}, UnsetMarker>): ProcedureBuilder<Context, object, {
user: {
id: string;
};
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
use((opts: {
ctx: {
user: {
id: string;
} | undefined;
};
type: ProcedureType;
path: string;
input: UnsetMarker;
getRawInput: GetRawInputFn;
meta: object | undefined;
signal: AbortSignal | undefined;
batchIndex: number;
next: {
(): Promise<MiddlewareResult<object>>;
<$ContextOverride>(opts: {
ctx?: $ContextOverride | undefined;
input?: unknown;
}): Promise<MiddlewareResult<$ContextOverride>>;
(opts: {
getRawInput: GetRawInputFn;
}): Promise<...>;
};
}
opts) => {
const { const ctx: {
user: {
id: string;
} | undefined;
}
ctx } = opts: {
ctx: {
user: {
id: string;
} | undefined;
};
type: ProcedureType;
path: string;
input: UnsetMarker;
getRawInput: GetRawInputFn;
meta: object | undefined;
signal: AbortSignal | undefined;
batchIndex: number;
next: {
(): Promise<MiddlewareResult<object>>;
<$ContextOverride>(opts: {
ctx?: $ContextOverride | undefined;
input?: unknown;
}): Promise<MiddlewareResult<$ContextOverride>>;
(opts: {
getRawInput: GetRawInputFn;
}): Promise<...>;
};
}
opts;
if (!const ctx: {
user: {
id: string;
} | undefined;
}
ctx.user: {
id: string;
} | undefined
user) {
throw new new TRPCError(opts: {
message?: string;
code: TRPC_ERROR_CODE_KEY;
cause?: unknown;
}): TRPCError
TRPCError({
code: "UNAUTHORIZED" | "PARSE_ERROR" | "BAD_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "PRECONDITION_REQUIRED" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST"code: 'UNAUTHORIZED',
message?: string | undefinedmessage: 'You are not authorized',
});
}
return opts: {
ctx: {
user: {
id: string;
} | undefined;
};
type: ProcedureType;
path: string;
input: UnsetMarker;
getRawInput: GetRawInputFn;
meta: object | undefined;
signal: AbortSignal | undefined;
batchIndex: number;
next: {
(): Promise<MiddlewareResult<object>>;
<$ContextOverride>(opts: {
ctx?: $ContextOverride | undefined;
input?: unknown;
}): Promise<MiddlewareResult<$ContextOverride>>;
(opts: {
getRawInput: GetRawInputFn;
}): Promise<...>;
};
}
opts.next: <{
user: {
id: string;
};
}>(opts: {
ctx?: {
user: {
id: string;
};
} | undefined;
input?: unknown;
}) => Promise<MiddlewareResult<{
user: {
id: string;
};
}>> (+2 overloads)
next({
ctx?: {
user: {
id: string;
};
} | undefined
ctx: {
// Infers that the `user` is non-nullable
user: {
id: string;
}
user: const ctx: {
user: {
id: string;
} | undefined;
}
ctx.user: {
id: string;
}
user,
},
});
});
const const router: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router = const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder
<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>(_: {
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}) => BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router({
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>
secret: const protectedProcedure: ProcedureBuilder<Context, object, {
user: {
id: string;
};
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
protectedProcedure.ProcedureBuilder<Context, object, { user: { id: string; }; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<{
id: string;
}>(resolver: ProcedureResolver<Context, object, {
user: {
id: string;
};
}, UnsetMarker, UnsetMarker, {
id: string;
}>): QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>
query((opts: ProcedureResolverOptions<Context, object, {
user: {
id: string;
};
}, UnsetMarker>
opts) => opts: ProcedureResolverOptions<Context, object, {
user: {
id: string;
};
}, UnsetMarker>
opts.ProcedureResolverOptions<Context, object, { user: { id: string; }; }, UnsetMarker>.ctx: {
user: {
id: string;
};
}
ctx.user: {
id: string;
}
user),
});
{
// ❌ this will return an error because there isn't the right context param
const const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
caller = const router: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router.Router<{ ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<...>>.createCaller: (ctx: Context | (() => MaybePromise<Context>), options?: {
onError?: RouterCallerErrorHandler<Context> | undefined;
signal?: AbortSignal;
} | undefined) => DecorateRouterRecord<DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
createCaller({});
const const result: {
id: string;
}
result = await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
caller.secret: (input: void) => Promise<{
id: string;
}>
secret();
}
{
// ✅ this will work because user property is present inside context param
const const authorizedCaller: DecorateRouterRecord<DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
authorizedCaller = const router: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router.Router<{ ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<...>>.createCaller: (ctx: Context | (() => MaybePromise<Context>), options?: {
onError?: RouterCallerErrorHandler<Context> | undefined;
signal?: AbortSignal;
} | undefined) => DecorateRouterRecord<DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
createCaller({
user?: {
id: string;
} | undefined
user: {
id: stringid: 'KATT',
},
});
const result = await const authorizedCaller: DecorateRouterRecord<DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
id: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
authorizedCaller.secret: (input: void) => Promise<{
id: string;
}>
secret();}Example for a Next.js API endpoint
type type ResponseData = {
data?: {
postTitle: string;
};
error?: {
message: string;
};
}
ResponseData = {
data?: {
postTitle: string;
} | undefined
data?: {
postTitle: stringpostTitle: string;
};
error?: {
message: string;
} | undefined
error?: {
message: stringmessage: string;
};
};
export default async (
req: NextApiRequestreq: NextApiRequest,
res: NextApiResponse<ResponseData>res: type NextApiResponse<Data = any> = ServerResponse<IncomingMessage> & {
send: Send<Data>;
json: Send<Data>;
status: (statusCode: number) => NextApiResponse<Data>;
redirect(url: string): NextApiResponse<Data>;
redirect(status: number, url: string): NextApiResponse<Data>;
setDraftMode: (options: {
enable: boolean;
}) => NextApiResponse<Data>;
setPreviewData: (data: object | string, options?: {
maxAge?: number;
path?: string;
}) => NextApiResponse<Data>;
clearPreviewData: (options?: {
path?: string;
}) => NextApiResponse<Data>;
revalidate: (urlPath: string, opts?: {
unstable_onlyGenerated?: boolean;
}) => Promise<void>;
}
NextApiResponse<type ResponseData = {
data?: {
postTitle: string;
};
error?: {
message: string;
};
}
ResponseData>,
) => {
/** We want to simulate an error, so we pick a post ID that does not exist in the database. */
const const postId: stringpostId = `this-id-does-not-exist-${var Math: MathMath.Math.random(): numberrandom()}`;
const const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller = const appRouter: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
appRouter.Router<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{ post: BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<...>>; }>>.createCaller: (ctx: object | (() => MaybePromise<object>), options?: {
onError?: RouterCallerErrorHandler<object> | undefined;
signal?: AbortSignal;
} | undefined) => DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
createCaller({});
try {
// the server-side call
const const postResult: {
id: string;
title: string;
}
postResult = await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>
caller.post: DecorateRouterRecord<DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: {
id: string;
};
output: {
id: string;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.byId: (input: {
id: string;
}) => Promise<{
id: string;
title: string;
}>
byId({ id: stringid: const postId: stringpostId });
res: NextApiResponse<ResponseData>res.status: (statusCode: number) => NextApiResponse<ResponseData>status(200).json: (body: ResponseData) => voidjson({ data?: {
postTitle: string;
} | undefined
data: { postTitle: stringpostTitle: const postResult: {
id: string;
title: string;
}
postResult.title: stringtitle } });
} catch (function (local var) cause: unknowncause) {
// If this a tRPC error, we can extract additional information.
if (function (local var) cause: unknowncause instanceof class TRPCErrorTRPCError) {
// We can get the specific HTTP status code coming from tRPC (e.g. 404 for `NOT_FOUND`).
const const httpStatusCode: numberhttpStatusCode = function getHTTPStatusCodeFromError(error: TRPCError): numbergetHTTPStatusCodeFromError(function (local var) cause: TRPCErrorcause);
res: NextApiResponse<ResponseData>res.status: (statusCode: number) => NextApiResponse<ResponseData>status(const httpStatusCode: numberhttpStatusCode).json: (body: ResponseData) => voidjson({ error?: {
message: string;
} | undefined
error: { message: stringmessage: function (local var) cause: TRPCErrorcause.Error.message: stringmessage } });
return;
}
// This is not a tRPC error, so we don't have specific information.
res: NextApiResponse<ResponseData>res.status: (statusCode: number) => NextApiResponse<ResponseData>status(500).json: (body: ResponseData) => voidjson({
error?: {
message: string;
} | undefined
error: { message: stringmessage: `Error while accessing post with ID ${const postId: stringpostId}` },
});
}
};Error handling
A procedure called through a caller throws its error as a TRPCError; errors that weren't thrown as one become an INTERNAL_SERVER_ERROR with the original error as their cause.
The createCaller function can also take an error handler through the onError option, for example to log or report errors. The caller still throws the error after the handler runs. Its options also take a signal, which your procedures receive as opts.signal.
The handler is called with the same arguments as an error formatter would be, except for the shape field:
interface OnErrorShape {
OnErrorShape.ctx: unknownctx: unknown;
OnErrorShape.error: TRPCErrorerror: class TRPCErrorTRPCError;
OnErrorShape.path: string | undefinedpath: string | undefined;
OnErrorShape.input: unknowninput: unknown;
OnErrorShape.type: "query" | "mutation" | "subscription" | "unknown"type: 'query' | 'mutation' | 'subscription' | 'unknown';
}import { const initTRPC: TRPCBuilder<object, object>initTRPC } from '@trpc/server';
import { import zz } from 'zod';
const const t: TRPCRootObject<{
foo?: "bar";
}, object, RuntimeConfigOptions<{
foo?: "bar";
}, object>, {
ctx: {
foo?: "bar";
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC
.TRPCBuilder<object, object>.context<{
foo?: "bar";
}>(): TRPCBuilder<{
foo?: "bar";
}, object>
context<{
foo?: "bar" | undefinedfoo?: 'bar';
}>()
.TRPCBuilder<{ foo?: "bar"; }, object>.create<RuntimeConfigOptions<{
foo?: "bar";
}, object>>(opts?: RuntimeConfigOptions<{
foo?: "bar";
}, object> | undefined): TRPCRootObject<{
foo?: "bar";
}, object, RuntimeConfigOptions<{
foo?: "bar";
}, object>, {
ctx: {
foo?: "bar";
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
create();
const const router: BuiltRouter<{
ctx: {
foo?: "bar";
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router = const t: TRPCRootObject<{
foo?: "bar";
}, object, RuntimeConfigOptions<{
foo?: "bar";
}, object>, {
ctx: {
foo?: "bar";
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<{ foo?: "bar"; }, object, RuntimeConfigOptions<{ foo?: "bar"; }, object>, { ctx: { foo?: "bar"; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder
<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>(_: {
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}) => BuiltRouter<{
ctx: {
foo?: "bar";
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router({
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>
greeting: const t: TRPCRootObject<{
foo?: "bar";
}, object, RuntimeConfigOptions<{
foo?: "bar";
}, object>, {
ctx: {
foo?: "bar";
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<{ foo?: "bar"; }, object, RuntimeConfigOptions<{ foo?: "bar"; }, object>, { ctx: { foo?: "bar"; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<{
foo?: "bar";
}, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
procedure.ProcedureBuilder<{ foo?: "bar"; }, 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<{
foo?: "bar";
}, object, object, {
name: string;
}, {
name: string;
}, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
input(import zz.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>
object({ name: z.ZodStringname: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() })).ProcedureBuilder<{ foo?: "bar"; }, object, object, { name: string; }, { name: string; }, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<string>(resolver: ProcedureResolver<{
foo?: "bar";
}, object, object, {
name: string;
}, UnsetMarker, string>): QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>
query((opts: ProcedureResolverOptions<{
foo?: "bar";
}, object, object, {
name: string;
}>
opts) => {
if (opts: ProcedureResolverOptions<{
foo?: "bar";
}, object, object, {
name: string;
}>
opts.ProcedureResolverOptions<{ foo?: "bar"; }, object, object, { name: string; }>.input: {
name: string;
}
input.name: stringname === 'invalid') {
throw new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error('Invalid name');
}
return `Hello ${opts: ProcedureResolverOptions<{
foo?: "bar";
}, object, object, {
name: string;
}>
opts.ProcedureResolverOptions<{ foo?: "bar"; }, object, object, { name: string; }>.input: {
name: string;
}
input.name: stringname}`;
}),
});
const const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
caller = const router: BuiltRouter<{
ctx: {
foo?: "bar";
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router.Router<{ ctx: { foo?: "bar"; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<...>>.createCaller: (ctx: {
foo?: "bar";
} | (() => MaybePromise<{
foo?: "bar";
}>), options?: {
onError?: RouterCallerErrorHandler<{
foo?: "bar";
}> | undefined;
signal?: AbortSignal;
} | undefined) => DecorateRouterRecord<DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
createCaller(
{
/* context */
},
{
onError?: RouterCallerErrorHandler<{
foo?: "bar";
}> | undefined
onError: (opts: ErrorHandlerOptions<{
foo?: "bar";
}>
opts) => {
var console: Consoleconsole.Console.error(message?: any, ...optionalParams: any[]): void (+1 overload)error('An error occurred:', opts: ErrorHandlerOptions<{
foo?: "bar";
}>
opts.ErrorHandlerOptions<{ foo?: "bar"; }>.error: TRPCErrorerror);
},
},
);
// The following will log "An error occurred: Error: Invalid name", and then throw the error
await const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{
greeting: QueryProcedure<{
input: {
name: string;
};
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
caller.greeting: (input: {
name: string;
}) => Promise<string>
greeting({ name: stringname: 'invalid' });