Skip to content
oRPC
Esc
navigateopen⌘Jpreview
On this page

Procedure Contract

Define procedure contracts that describe input, output, errors, and metadata without business logic, keeping implementations aligned.

Overview

import { const oc: ContractBuilder<object>
The oRPC contract builder. Chain methods like `.input`, `.errors`, and `.output` to define procedure contracts, then compose them into router contracts.
@see{@link https://orpc.dev/docs/contract/procedure Procedure Contract}
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 oRPC contract builder. Chain methods like `.input`, `.errors`, and `.output` to define procedure contracts, then compose them into router contracts.
@see{@link https://orpc.dev/docs/contract/procedure Procedure Contract}
oc
.ContractBuilder<object>.meta(...plugins: MetaPlugin<InitialInputSchema, InitialOutputSchema, object>[]): ContractBuilder<object>
Applies metadata plugins to contracts built from this builder.
@see{@link https://orpc.dev/docs/contract/procedure#metadata Procedure Contract - Metadata}
meta
(const someMeta: AnyMetaPluginsomeMeta) // <- attach metadata
.
ContractBuilder<object>.errors<{
    NOT_FOUND: {};
}>(errors: {
    NOT_FOUND: {};
}): ContractBuilder<{
    NOT_FOUND: {};
}>
Defines typesafe errors that implementations of this contract can throw.
@see{@link https://orpc.dev/docs/contract/procedure#typesafe-errors Procedure Contract - Typesafe Errors}
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: {};
}>
Defines the input schema used to validate and type the procedure input.
@see{@link https://orpc.dev/docs/contract/procedure#inputoutput-validation Procedure Contract - Input/Output Validation}
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: {};
}>
Defines the output schema used to validate and type the procedure output.
@see{@link https://orpc.dev/docs/contract/procedure#inputoutput-validation Procedure Contract - Input/Output Validation}
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.

Last updated on August 8, 2026

Was this page helpful?