Procedure Contract
Procedure contracts define the expected shape of a procedure without including any business logic. They are useful for documentation, testing, and keeping multiple implementations of the same procedure aligned.
Overview
import { const oc: ContractBuilder<object>The contract builder — the entry point for defining procedure and router contracts
(input/output schemas, errors, and metadata) without any business logic.oc } from '@orpc/contract'
const const example: ProcedureContractBuilderWithInputOutput<z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>, z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>, {
NOT_FOUND: {};
}>
example = const oc: ContractBuilder<object>The contract builder — the entry point for defining procedure and router contracts
(input/output schemas, errors, and metadata) without any business logic.oc
.ContractBuilder<object>.meta(...plugins: MetaPlugin<InitialInputSchema, InitialOutputSchema, object>[]): ContractBuilder<object>meta(const someMeta: AnyMetaPluginsomeMeta) // <- attach metadata
.ContractBuilder<object>.errors<{
NOT_FOUND: {};
}>(errors: {
NOT_FOUND: {};
}): ContractBuilder<{
NOT_FOUND: {};
}>
errors({ type NOT_FOUND: {}NOT_FOUND: {} }) // <- define errors
.ContractBuilder<{ NOT_FOUND: {}; }>.input<z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>): ProcedureContractBuilderWithInput<z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>, {
NOT_FOUND: {};
}>
input(import zz.function object<{
id: z.ZodNumber;
name: z.ZodString;
}>(shape?: {
id: z.ZodNumber;
name: z.ZodString;
} | undefined, params?: string | {
error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>
object({ id: z.ZodNumberid: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber(), name: z.ZodStringname: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() })) // <- input validation
.ProcedureContractBuilderWithInput<ZodObject<{ id: ZodNumber; name: ZodString; }, $strip>, { NOT_FOUND: {}; }>.output<z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>>(schema: z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>): ProcedureContractBuilderWithInputOutput<z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>, z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>, {
NOT_FOUND: {};
}>
output(import zz.function object<{
id: z.ZodNumber;
name: z.ZodString;
}>(shape?: {
id: z.ZodNumber;
name: z.ZodString;
} | undefined, params?: string | {
error?: string | z.core.$ZodErrorMap<NonNullable<z.core.$ZodIssueInvalidType<unknown> | z.core.$ZodIssueUnrecognizedKeys>> | undefined;
message?: string | undefined | undefined;
} | undefined): z.ZodObject<{
id: z.ZodNumber;
name: z.ZodString;
}, z.core.$strip>
object({ id: z.ZodNumberid: import zz.function number(params?: string | z.core.$ZodNumberParams): z.ZodNumbernumber(), name: z.ZodStringname: import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string() })) // <- output validation
Metadata
Use .meta to attach metadata to a contract. Middleware and plugins can read it later when you implement the contract. Learn more in the Metadata documentation.
Typesafe Errors
Use .errors to define the errors a contract can produce. These errors can be thrown from handlers or middleware when you implement the contract and remain properly typed on the client. Learn more in the Typesafe Error Handling documentation.
Input/Output Validation
oRPC supports Zod, Valibot, Arktype, and any other Standard Schema library for validation.
Multiple Schemas
.input and .output can be called multiple times. Each call adds another schema instead of replacing an earlier one.
const example = oc
.input(z.looseObject({ name: z.string() }))
.input(z.looseObject({ id: z.number() }))
.output(z.looseObject({ name: z.string() }))
.output(z.looseObject({ id: z.number() }))
type Utility
For simple use cases without external libraries, use oRPC’s built-in type utility. It takes a mapping function as its first argument:
import { type } from '@orpc/contract'
const example = oc
.input(type<{ value: number }>())
.output(type<{ value: number }, number>(({ value }) => value))
Reusability
Each builder call creates a new instance, which avoids reference issues and makes contracts easy to reuse and extend.
const pub = oc // Base setup for procedures that publish
const authed = pub.meta(requireAuthMeta) // Extends 'pub' with authentication
const pubExample = pub
.input(z.object({ name: z.string() }))
const authedExample = authed
.input(z.object({ id: z.number() }))
This pattern helps prevent duplication while maintaining flexibility.