Router Contract
Router contracts define the shape of a router without including any business logic. Use them for documentation, testing, and keeping multiple implementations of the same router aligned.
INFO
A standalone procedure contract is also a router contract, so you can use the same features with individual procedure contracts.
Overview
Define a router contract as a plain JavaScript object where each key maps to a procedure contract:
import { oc } from '@orpc/contract'
const ping = oc.output(z.string())
const pong = oc.output(z.string())
export const router = {
ping,
pong,
nested: { ping, pong }
}WARNING
For compatibility, do not use these router keys: then, bind, valueOf, toString, toJSON.
Extending Router
You can extend a router contract with shared configuration, such as attaching metadata to every procedure:
const router = oc.meta(requireAuthMeta).router({
ping,
pong,
nested: {
ping,
pong,
}
})Router to Contract
A normal router can be used as a contract router as long as it does not include a lazy router. If necessary, use unlazyRouter to fully resolve it and make it contract-compatible.
import { unlazyRouter } from '@orpc/server'
const compatibleContract = await unlazyRouter(router)Safely Importing Router on the Client
Sometimes you need to import the contract on the client, for example when using OpenAPI Link. If you derive the contract from a router, importing it directly can be heavy and may expose internal logic. To avoid this, follow the steps below to safely minify and export the contract.
Minify the Contract Router and Export to JSON
tsimport fs from 'node:fs' import { unlazyRouter } from '@orpc/server' import { minifyRouterContract } from '@orpc/contract' const compatibleContract = await unlazyRouter(router) const minifiedRouter = minifyRouterContract(compatibleContract) fs.writeFileSync('./contract.json', JSON.stringify(minifiedRouter))INFO
minifyRouterContractpreserves only the metadata needed by the client; all other data is stripped out.Import the Contract JSON on the Client Side
tsimport contract from './contract.json' const link = new OpenAPILink(contract as typeof router)INFO
Cast
contracttotypeof routerto preserve type safety, since standard schema types cannot be serialized to JSON and must be cast manually.
Utilities
INFO
A standalone procedure contract is also a router contract, so these utilities work with individual procedure contracts too.
Infer Router Contract Inputs
Infers the input type of each procedure contract in a router contract.
import type { InferRouterContractInputs } from '@orpc/contract'
export type Inputs = InferRouterContractInputs<typeof contract>
type FindPlanetInput = Inputs['planet']['find']Infer Router Contract Outputs
Infers the output type of each procedure contract in a router contract.
import type { InferRouterContractOutputs } from '@orpc/contract'
export type Outputs = InferRouterContractOutputs<typeof contract>
type FindPlanetOutput = Outputs['planet']['find']Infer Router Contract Error Map
Collects the error maps from every procedure contract in a router contract into a single type.
import type { InferRouterContractErrorMap } from '@orpc/contract'
export type ErrorMap = InferRouterContractErrorMap<typeof contract>Infer Router Contract Errors
Infers the throwable errors each procedure contract in a router contract can describe.
import type { InferRouterContractErrors } from '@orpc/contract'
export type Errors = InferRouterContractErrors<typeof contract>
type FindPlanetError = Errors['planet']['find']Infer Router Contract Error
Infers all possible throwable errors the entire router contract can describe. This is useful when you want a single type for contract-wide error handling.
import type { InferRouterContractError } from '@orpc/contract'
export type ContractError = InferRouterContractError<typeof contract>
