---
title: "Router Contract"
description: "Define router contracts that describe the shape of a router without business logic, useful for documentation, testing, and aligned implementations."
---

:::info
A standalone [procedure contract](/docs/contract/procedure) 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:

```ts twoslash
import { z } from 'zod'
// ---cut---
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:

```ts
const router = oc.meta(requireAuthMeta).router({
  ping,
  pong,
  nested: {
    ping,
    pong,
  }
})
```

## Router to Contract

A normal [router](/docs/router) can be used as a contract router as long as it does not include a [lazy router](/docs/router#lazy-router). If necessary, use `unlazyRouter` to fully resolve it and make it contract-compatible.

```ts
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](/docs/openapi/link). If you derive the contract from a [router](/docs/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.

1.  **Minify the Contract Router and Export to JSON**

    ```ts
    import 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
    `minifyRouterContract` preserves only the metadata needed by the client; all other data is stripped out.
    :::

2.  **Import the Contract JSON on the Client Side**

    ```ts
    import contract from './contract.json' // [!code highlight]

    const link = new OpenAPILink(contract as typeof router)
    ```

    ::: info
    Cast `contract` to `typeof router` to preserve type safety, since standard schema types cannot be serialized to JSON and must be cast manually.
    :::

## Utilities

:::info
A standalone [procedure contract](/docs/contract/procedure) 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.

```ts twoslash
import type { contract } from './shared/planet'
// ---cut---
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.

```ts twoslash
import type { contract } from './shared/planet'
// ---cut---
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.

```ts twoslash
import type { contract } from './shared/planet'
// ---cut---
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.

```ts twoslash
import type { contract } from './shared/planet'
// ---cut---
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.

```ts twoslash
import type { contract } from './shared/planet'
// ---cut---
import type { InferRouterContractError } from '@orpc/contract'

export type ContractError = InferRouterContractError<typeof contract>
```
