Skip to content
tRPC

Partners

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();
const postList: Post[]

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' });
const result: string

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');
const result: string[]

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
();
const result: {
    id: string;
}
}

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' });