Skip to content
tRPC

Partners

Define Procedures

On this page

A procedure is a function which is exposed to the client, it can be one of:

  • a Query - used to fetch data, generally does not change any data
  • a Mutation - used to send data, often for create/update/delete purposes
  • a Subscription - you might not need this, and we have dedicated documentation

Procedures in tRPC are very flexible primitives to create backend functions. They use an immutable builder pattern, which means you can create reusable base procedures that share functionality among multiple procedures.

Writing procedures

The t object you create during tRPC setup returns an initial t.procedure which all other procedures are built on:

import { const initTRPC: TRPCBuilder<object, object>initTRPC } from '@trpc/server';
import { import zz } from 'zod';

const 
const t: TRPCRootObject<{
    signGuestBook: () => Promise<void>;
}, object, RuntimeConfigOptions<{
    signGuestBook: () => Promise<void>;
}, object>, {
    ctx: {
        signGuestBook: () => Promise<void>;
    };
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
t
= const initTRPC: TRPCBuilder<object, object>initTRPC.
TRPCBuilder<object, object>.context<{
    signGuestBook: () => Promise<void>;
}>(): TRPCBuilder<{
    signGuestBook: () => Promise<void>;
}, object>
context
<{ signGuestBook: () => Promise<void>signGuestBook: () => interface Promise<T>Promise<void> }>().
TRPCBuilder<{ signGuestBook: () => Promise<void>; }, object>.create<RuntimeConfigOptions<{
    signGuestBook: () => Promise<void>;
}, object>>(opts?: RuntimeConfigOptions<{
    signGuestBook: () => Promise<void>;
}, object> | undefined): TRPCRootObject<{
    signGuestBook: () => Promise<void>;
}, object, RuntimeConfigOptions<{
    signGuestBook: () => Promise<void>;
}, object>, {
    ctx: {
        signGuestBook: () => Promise<void>;
    };
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
create
();
export const
const router: RouterBuilder<{
    ctx: {
        signGuestBook: () => Promise<void>;
    };
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
router
=
const t: TRPCRootObject<{
    signGuestBook: () => Promise<void>;
}, object, RuntimeConfigOptions<{
    signGuestBook: () => Promise<void>;
}, object>, {
    ctx: {
        signGuestBook: () => Promise<void>;
    };
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
t
.
TRPCRootObject<{ signGuestBook: () => Promise<void>; }, object, RuntimeConfigOptions<{ signGuestBook: () => Promise<void>; }, object>, { ctx: { signGuestBook: () => Promise<void>; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder<{
    ctx: {
        signGuestBook: () => Promise<void>;
    };
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
router
;
export const
const publicProcedure: ProcedureBuilder<{
    signGuestBook: () => Promise<void>;
}, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
publicProcedure
=
const t: TRPCRootObject<{
    signGuestBook: () => Promise<void>;
}, object, RuntimeConfigOptions<{
    signGuestBook: () => Promise<void>;
}, object>, {
    ctx: {
        signGuestBook: () => Promise<void>;
    };
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}>
t
.
TRPCRootObject<{ signGuestBook: () => Promise<void>; }, object, RuntimeConfigOptions<{ signGuestBook: () => Promise<void>; }, object>, { ctx: { signGuestBook: () => Promise<void>; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<{
    signGuestBook: () => Promise<void>;
}, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
procedure
;
const
const appRouter: BuiltRouter<{
    ctx: {
        signGuestBook: () => Promise<void>;
    };
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    hello: QueryProcedure<{
        input: void;
        output: {
            message: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    goodbye: MutationProcedure<{
        input: void;
        output: {
            message: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>
appRouter
=
const router: RouterBuilder
<{
    hello: QueryProcedure<{
        input: void;
        output: {
            message: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    goodbye: MutationProcedure<{
        input: void;
        output: {
            message: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>(_: {
    hello: QueryProcedure<{
        input: void;
        output: {
            message: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    goodbye: MutationProcedure<{
        input: void;
        output: {
            message: string;
        };
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}) => BuiltRouter<...>
router
({
// Queries are the best place to fetch data
hello: QueryProcedure<{
    input: void;
    output: {
        message: string;
    };
    meta: object;
    errorShape: DefaultErrorShape;
}>
hello
:
const publicProcedure: ProcedureBuilder<{
    signGuestBook: () => Promise<void>;
}, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
publicProcedure
.
ProcedureBuilder<{ signGuestBook: () => Promise<void>; }, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<{
    message: string;
}>(resolver: ProcedureResolver<{
    signGuestBook: () => Promise<void>;
}, object, object, UnsetMarker, UnsetMarker, {
    message: string;
}>): QueryProcedure<{
    input: void;
    output: {
        message: string;
    };
    meta: object;
    errorShape: DefaultErrorShape;
}>
query
(() => {
return { message: stringmessage: 'hello world', }; }), // Mutations are the best place to do things like updating a database
goodbye: MutationProcedure<{
    input: void;
    output: {
        message: string;
    };
    meta: object;
    errorShape: DefaultErrorShape;
}>
goodbye
:
const publicProcedure: ProcedureBuilder<{
    signGuestBook: () => Promise<void>;
}, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
publicProcedure
.
ProcedureBuilder<{ signGuestBook: () => Promise<void>; }, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.mutation<{
    message: string;
}>(resolver: ProcedureResolver<{
    signGuestBook: () => Promise<void>;
}, object, object, UnsetMarker, UnsetMarker, {
    message: string;
}>): MutationProcedure<{
    input: void;
    output: {
        message: string;
    };
    meta: object;
    errorShape: DefaultErrorShape;
}>
mutation
(async (
opts: ProcedureResolverOptions<{
    signGuestBook: () => Promise<void>;
}, object, object, UnsetMarker>
opts
) => {
await
opts: ProcedureResolverOptions<{
    signGuestBook: () => Promise<void>;
}, object, object, UnsetMarker>
opts
.
ProcedureResolverOptions<{ signGuestBook: () => Promise<void>; }, object, object, UnsetMarker>.ctx: {
    signGuestBook: () => Promise<void>;
}
ctx
.signGuestBook: () => Promise<void>signGuestBook();
return { message: stringmessage: 'goodbye!', }; }), });

Reusable "Base Procedures"

As a general pattern we recommend you rename and export t.procedure as publicProcedure, which then makes room for you to create other named procedures for specific use cases and export those too. This pattern is called "base procedures" and is a key pattern for code and behaviour re-use in tRPC; every application is likely to need it.

In the below code, we're using reusable base procedures to build common use-cases for our app - we're making a reusable base procedures for logged in users (authedProcedure) & another base procedure that takes an organizationId and validates that a user is part of that organization.

This is a simplified example; in practice you may want to use some combination of Headers, Context, Middleware, and Metadata, to authenticate and authorize your users.

import { const initTRPC: TRPCBuilder<object, object>initTRPC, class TRPCErrorTRPCError } from '@trpc/server';
import { import zz } from 'zod';

type 
type Organization = {
    id: string;
    name: string;
}
Organization
= {
id: stringid: string; name: stringname: string; }; type
type Membership = {
    role: "ADMIN" | "MEMBER";
    Organization: Organization;
}
Membership
= {
role: "ADMIN" | "MEMBER"role: 'ADMIN' | 'MEMBER'; type Organization: OrganizationOrganization:
type Organization = {
    id: string;
    name: string;
}
Organization
;
}; type
type User = {
    id: string;
    memberships: Membership[];
}
User
= {
id: stringid: string; memberships: Membership[]memberships:
type Membership = {
    role: "ADMIN" | "MEMBER";
    Organization: Organization;
}
Membership
[];
}; type
type Context = {
    user: User | null;
}
Context
= {
/** * User is nullable */ user: User | nulluser:
type User = {
    id: string;
    memberships: Membership[];
}
User
| null;
}; 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: User | null;
}
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
();
export 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;
// procedure that asserts that the user is logged in export const
const authedProcedure: ProcedureBuilder<Context, object, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
authedProcedure
=
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: User;
}>(fn: MiddlewareBuilder<{
    user: User | null;
}, object, {
    user: User;
}, UnsetMarker> | MiddlewareFunction<Context, object, object, {
    user: User;
}, UnsetMarker>): ProcedureBuilder<Context, object, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
use
(async function
function (local function) isAuthed(opts: {
    ctx: {
        user: User | null;
    };
    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<...>;
    };
}): Promise<...>
isAuthed
(
opts: {
    ctx: {
        user: User | null;
    };
    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: User | null;
}
ctx
} =
opts: {
    ctx: {
        user: User | null;
    };
    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
;
// `ctx.user` is nullable if (!
const ctx: {
    user: User | null;
}
ctx
.user) {
user: User | null
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' });
} return
opts: {
    ctx: {
        user: User | null;
    };
    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: User;
}>(opts: {
    ctx?: {
        user: User;
    } | undefined;
    input?: unknown;
}) => Promise<MiddlewareResult<{
    user: User;
}>> (+2 overloads)
next
({
ctx?: {
    user: User;
} | undefined
ctx
: {
// ✅ user value is known to be non-null now user: Useruser:
const ctx: {
    user: User | null;
}
ctx
.user: Useruser,
}, }); }); // procedure that asserts a user is a member of a specific organization export const
const organizationProcedure: ProcedureBuilder<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
}, {
    organizationId: string;
}, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
organizationProcedure
=
const authedProcedure: ProcedureBuilder<Context, object, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
authedProcedure
.
ProcedureBuilder<Context, object, { user: User; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.input<z.ZodObject<{
    organizationId: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    organizationId: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<Context, object, {
    user: User;
}, {
    organizationId: string;
}, {
    organizationId: string;
}, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
input
(import zz.
function object<{
    organizationId: z.ZodString;
}>(shape?: {
    organizationId: 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<{
    organizationId: z.ZodString;
}, z.core.$strip>
object
({ organizationId: z.ZodStringorganizationId: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() }))
.
ProcedureBuilder<Context, object, { user: User; }, { organizationId: string; }, { organizationId: string; }, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.use<{
    Organization: Organization;
}>(fn: MiddlewareBuilder<{
    user: User;
}, object, {
    Organization: Organization;
}, {
    organizationId: string;
}> | MiddlewareFunction<Context, object, {
    user: User;
}, {
    Organization: Organization;
}, {
    organizationId: string;
}>): ProcedureBuilder<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
}, {
    organizationId: string;
}, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
use
(function
function (local function) isMemberOfOrganization(opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: {
        organizationId: string;
    };
    getRawInput: GetRawInputFn;
    meta: object | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}): Promise<...>
isMemberOfOrganization
(
opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: {
        organizationId: string;
    };
    getRawInput: GetRawInputFn;
    meta: object | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}
opts
) {
const const membership: Membership | undefinedmembership =
opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: {
        organizationId: string;
    };
    getRawInput: GetRawInputFn;
    meta: object | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}
opts
.
ctx: {
    user: User;
}
ctx
.user: Useruser.memberships: Membership[]memberships.Array<Membership>.find(predicate: (value: Membership, index: number, obj: Membership[]) => unknown, thisArg?: any): Membership | undefined (+1 overload)find(
(m: Membershipm) => m: Membershipm.type Organization: OrganizationOrganization.id: stringid ===
opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: {
        organizationId: string;
    };
    getRawInput: GetRawInputFn;
    meta: object | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}
opts
.
input: {
    organizationId: string;
}
input
.organizationId: stringorganizationId,
); if (!const membership: Membership | undefinedmembership) { 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: 'FORBIDDEN', }); } return
opts: {
    ctx: {
        user: User;
    };
    type: ProcedureType;
    path: string;
    input: {
        organizationId: string;
    };
    getRawInput: GetRawInputFn;
    meta: object | undefined;
    signal: AbortSignal | undefined;
    batchIndex: number;
    next: {
        (): Promise<MiddlewareResult<{
            user: User;
        }>>;
        <$ContextOverride>(opts: {
            ctx?: $ContextOverride | undefined;
            input?: unknown;
        }): Promise<MiddlewareResult<$ContextOverride>>;
        (opts: {
            getRawInput: GetRawInputFn;
        }): Promise<...>;
    };
}
opts
.
next: <{
    Organization: Organization;
}>(opts: {
    ctx?: {
        Organization: Organization;
    } | undefined;
    input?: unknown;
}) => Promise<MiddlewareResult<{
    Organization: Organization;
}>> (+2 overloads)
next
({
ctx?: {
    Organization: Organization;
} | undefined
ctx
: {
type Organization: OrganizationOrganization: const membership: Membershipmembership.type Organization: OrganizationOrganization, }, }); }); export const
const appRouter: BuiltRouter<{
    ctx: Context;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    whoami: QueryProcedure<{
        input: void;
        output: User;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    addMember: MutationProcedure<{
        input: {
            organizationId: string;
            email: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>
appRouter
=
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
<{
    whoami: QueryProcedure<{
        input: void;
        output: User;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    addMember: MutationProcedure<{
        input: {
            organizationId: string;
            email: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>(_: {
    whoami: QueryProcedure<{
        input: void;
        output: User;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
    addMember: MutationProcedure<{
        input: {
            organizationId: string;
            email: string;
        };
        output: string;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}) => BuiltRouter<...>
router
({
whoami: QueryProcedure<{
    input: void;
    output: User;
    meta: object;
    errorShape: DefaultErrorShape;
}>
whoami
:
const authedProcedure: ProcedureBuilder<Context, object, {
    user: User;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
authedProcedure
.
ProcedureBuilder<Context, object, { user: User; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<User>(resolver: ProcedureResolver<Context, object, {
    user: User;
}, UnsetMarker, UnsetMarker, User>): QueryProcedure<{
    input: void;
    output: User;
    meta: object;
    errorShape: DefaultErrorShape;
}>
query
(async (
opts: ProcedureResolverOptions<Context, object, {
    user: User;
}, UnsetMarker>
opts
) => {
// user is non-nullable here const { ctx } =
opts: ProcedureResolverOptions<Context, object, {
    user: User;
}, UnsetMarker>
opts
;
const ctx: {
    user: User;
}
return
const ctx: {
    user: User;
}
ctx
.user: Useruser;
}),
addMember: MutationProcedure<{
    input: {
        organizationId: string;
        email: string;
    };
    output: string;
    meta: object;
    errorShape: DefaultErrorShape;
}>
addMember
:
const organizationProcedure: ProcedureBuilder<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
}, {
    organizationId: string;
}, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
organizationProcedure
.
ProcedureBuilder<Context, object, { user: User; Organization: Organization; }, { organizationId: string; }, { organizationId: string; }, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.input<z.ZodObject<{
    email: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
    email: z.ZodString;
}, z.core.$strip>): ProcedureBuilder<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
    email: string;
}, {
    organizationId: string;
    email: string;
}, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
input
(
import zz.
function object<{
    email: z.ZodString;
}>(shape?: {
    email: 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<{
    email: z.ZodString;
}, z.core.$strip>
object
({
email: z.ZodStringemail: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string().ZodString.email(params?: string | z.core.$ZodCheckEmailParams): z.ZodStringemail(), }), ) .
ProcedureBuilder<Context, object, { user: User; Organization: Organization; }, { organizationId: string; email: string; }, { organizationId: string; email: string; }, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.mutation<string>(resolver: ProcedureResolver<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
    email: string;
}, UnsetMarker, string>): MutationProcedure<{
    input: {
        organizationId: string;
        email: string;
    };
    output: string;
    meta: object;
    errorShape: DefaultErrorShape;
}>
mutation
((
opts: ProcedureResolverOptions<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
    email: string;
}>
opts
) => {
// ctx contains the non-nullable user & the organization being queried const { ctx } =
opts: ProcedureResolverOptions<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
    email: string;
}>
opts
;
const ctx: {
    user: User;
    Organization: Organization;
}
// input includes the validated email of the user being invited & the validated organizationId const { input } =
opts: ProcedureResolverOptions<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
    email: string;
}>
opts
;
const input: {
    organizationId: string;
    email: string;
}
return '...'; }), });

Inferring the options type of a "Base Procedure"

In addition to being able to infer the input and output types of a procedure, you can also infer the options type of a specific procedure builder (or base procedure) using inferProcedureBuilderResolverOptions.

This type helper is useful for declaring a type to a function's parameters. Like for example, separating the procedure's handler (main execution code) from its definition at the router, or for creating a helper function that works with multiple procedures.

async function function getMembersOfOrganization(opts: inferProcedureBuilderResolverOptions<typeof organizationProcedure>): Promise<any>getMembersOfOrganization(
  
opts: ProcedureResolverOptions<Context, object, {
    user: User;
    Organization: Organization;
}, {
    [x: string]: unknown;
    organizationId: string;
}>
opts
:
type inferProcedureBuilderResolverOptions<TProcedureBuilder extends AnyProcedureBuilder> = TProcedureBuilder extends ProcedureBuilder<infer TContext, infer TMeta, infer TContextOverrides, infer _TInputIn, infer TInputOut, infer _TOutputIn, infer _TOutputOut, infer _TCaller extends boolean, infer _TErrorShape> ? ProcedureResolverOptions<TContext, TMeta, TContextOverrides, TInputOut extends UnsetMarker ? unknown : TInputOut extends object ? Simplify<TInputOut & {
    [keyAddedByInputCallFurtherDown: string]: unknown;
}> : TInputOut> : never
inferProcedureBuilderResolverOptions
<typeof
const organizationProcedure: ProcedureBuilder<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
}, {
    organizationId: string;
}, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
organizationProcedure
>,
) { // input and ctx are now correctly typed! const {
const ctx: {
    user: User;
    Organization: Organization;
}
ctx
,
const input: {
    [x: string]: unknown;
    organizationId: string;
}
input
} =
opts: ProcedureResolverOptions<Context, object, {
    user: User;
    Organization: Organization;
}, {
    [x: string]: unknown;
    organizationId: string;
}>
opts
;
return await let prisma: anyprisma.user.findMany({
where: {
    membership: {
        organizationId: string;
    };
}
where
: {
membership: {
    organizationId: string;
}
membership
: {
organizationId: stringorganizationId:
const ctx: {
    user: User;
    Organization: Organization;
}
ctx
.type Organization: OrganizationOrganization.id: stringid,
}, }, }); } export const
const appRouter: BuiltRouter<{
    ctx: Context;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    listMembers: QueryProcedure<{
        input: {
            organizationId: string;
        };
        output: any;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>
appRouter
=
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
<{
    listMembers: QueryProcedure<{
        input: {
            organizationId: string;
        };
        output: any;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>(_: {
    listMembers: QueryProcedure<{
        input: {
            organizationId: string;
        };
        output: any;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}) => BuiltRouter<{
    ctx: Context;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{
    listMembers: QueryProcedure<{
        input: {
            organizationId: string;
        };
        output: any;
        meta: object;
        errorShape: DefaultErrorShape;
    }>;
}>>
router
({
listMembers: QueryProcedure<{
    input: {
        organizationId: string;
    };
    output: any;
    meta: object;
    errorShape: DefaultErrorShape;
}>
listMembers
:
const organizationProcedure: ProcedureBuilder<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
}, {
    organizationId: string;
}, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
organizationProcedure
.
ProcedureBuilder<Context, object, { user: User; Organization: Organization; }, { organizationId: string; }, { organizationId: string; }, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<any>(resolver: ProcedureResolver<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
}, UnsetMarker, any>): QueryProcedure<{
    input: {
        organizationId: string;
    };
    output: any;
    meta: object;
    errorShape: DefaultErrorShape;
}>
query
(async (
opts: ProcedureResolverOptions<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
}>
opts
) => {
// use helper function! const const members: anymembers = await function getMembersOfOrganization(opts: inferProcedureBuilderResolverOptions<typeof organizationProcedure>): Promise<any>getMembersOfOrganization(
opts: ProcedureResolverOptions<Context, object, {
    user: User;
    Organization: Organization;
}, {
    organizationId: string;
}>
opts
);
return const members: anymembers; }), });

Subscriptions

For information on subscriptions, see our subscriptions guide.