OpenAPI Specification
oRPC uses the OpenAPI Specification to define APIs. It is fully compatible with OpenAPILink and OpenAPIHandler.
Installation
npm install @orpc/openapi@latest
yarn add @orpc/openapi@latest
pnpm add @orpc/openapi@latest
bun add @orpc/openapi@latest
deno install npm:@orpc/openapi@latest
Generating Specifications
oRPC supports OpenAPI 3.1.1 and integrates seamlessly with popular schema libraries like Zod, Valibot, and ArkType. You can generate specifications from either a Router or a Contract:
INFO
Interested in support for additional schema libraries? Let us know!
Want to create your own JSON schema converter?
You can use any existing X to JSON Schema
converter to add support for additional schema libraries. For example, if you want to use Valibot with oRPC (if not supported), you can create a custom converter to convert Valibot schemas into JSON Schema.
import type { AnySchema } from '@orpc/contract'
import type { ConditionalSchemaConverter, JSONSchema, SchemaConvertOptions } from '@orpc/openapi'
import type { ConversionConfig } from '@valibot/to-json-schema'
import { toJsonSchema } from '@valibot/to-json-schema'
export class ValibotToJsonSchemaConverter implements ConditionalSchemaConverter {
condition(schema: AnySchema | undefined): boolean {
return schema !== undefined && schema['~standard'].vendor === 'valibot'
}
convert(schema: AnySchema | undefined, _options: SchemaConvertOptions): [required: boolean, jsonSchema: Exclude<JSONSchema, boolean>] {
// Most JSON schema converters do not convert the `required` property separately, so returning `true` is acceptable here.
return [true, toJsonSchema(schema as any)]
}
}
INFO
It's recommended to use the built-in converters because the oRPC implementations handle many edge cases and supports every type that oRPC offers.
import { OpenAPIGenerator } from '@orpc/openapi'
import {
ZodToJsonSchemaConverter
} from '@orpc/zod' // <-- zod v3
import {
experimental_ZodToJsonSchemaConverter as ZodToJsonSchemaConverter
} from '@orpc/zod/zod4' // <-- zod v4
import {
experimental_ValibotToJsonSchemaConverter as ValibotToJsonSchemaConverter
} from '@orpc/valibot'
import {
experimental_ArkTypeToJsonSchemaConverter as ArkTypeToJsonSchemaConverter
} from '@orpc/arktype'
const openAPIGenerator = new OpenAPIGenerator({
schemaConverters: [
new ZodToJsonSchemaConverter(), // <-- if you use Zod
new ValibotToJsonSchemaConverter(), // <-- if you use Valibot
new ArkTypeToJsonSchemaConverter(), // <-- if you use ArkType
],
})
const specFromContract = await openAPIGenerator.generate(contract, {
info: {
title: 'My App',
version: '0.0.0',
},
})
const specFromRouter = await openAPIGenerator.generate(router, {
info: {
title: 'My App',
version: '0.0.0',
},
})
WARNING
Features prefixed with experimental_
are unstable and may lack some functionality.
Operation Metadata
You can enrich your API documentation by specifying operation metadata using the .route
or .tag
:
const ping = os
.route({
summary: 'the summary',
description: 'the description',
deprecated: false,
tags: ['tag'],
successDescription: 'the success description',
})
.handler(() => {})
// or append tag for entire router
const router = os.tag('planets').router({
// ...
})
Customizing Operation Objects
You can also extend the operation object using the .spec
helper for an error
or middleware
:
import { oo } from '@orpc/openapi'
const base = os.errors({
UNAUTHORIZED: oo.spec({
data: z.any(),
}, {
security: [{ 'api-key': [] }],
})
})
// OR in middleware
const requireAuth = oo.spec(
os.middleware(async ({ next, errors }) => {
throw new ORPCError('UNAUTHORIZED')
return next()
}),
{
security: [{ 'api-key': [] }]
}
)
Any procedure that includes the use above errors
or middleware
will automatically have the defined security
property applied
INFO
The .spec
helper accepts a callback as its second argument, allowing you to override the entire operation object.
@orpc/zod
Zod v4
File Schema
Zod v4 includes a native File
schema. oRPC will detect it automatically - no extra setup needed:
import * as z from 'zod'
const InputSchema = z.object({
file: oz.file(),
image: oz.file().mine(['image/png', 'image/jpeg']),
})
JSON Schema Customization
description
and examples
metadata are supported out of the box:
import * as z from 'zod'
const InputSchema = z.object({
name: z.string(),
}).meta({
description: 'User schema',
examples: [{ name: 'John' }],
})
For further customization, you can use the JSON_SCHEMA_REGISTRY
, JSON_SCHEMA_INPUT_REGISTRY
, and JSON_SCHEMA_OUTPUT_REGISTRY
:
import * as z from 'zod'
import {
experimental_JSON_SCHEMA_REGISTRY as JSON_SCHEMA_REGISTRY,
} from '@orpc/zod/zod4'
export const InputSchema = z.object({
name: z.string(),
})
JSON_SCHEMA_REGISTRY.add(InputSchema, {
description: 'User schema',
examples: [{ name: 'John' }],
// other options...
})
JSON_SCHEMA_INPUT_REGISTRY.add(InputSchema, {
// only for .input
})
JSON_SCHEMA_OUTPUT_REGISTRY.add(InputSchema, {
// only for .output
})
Zod v3
File Schema
In the File Upload/Download guide, z.instanceof
is used to describe file/blob schemas. However, this method prevents oRPC from recognizing file/blob schema. Instead, use the enhanced file schema approach:
import { z } from 'zod'
import { oz } from '@orpc/zod'
const InputSchema = z.object({
file: oz.file(),
image: oz.file().type('image/*'),
blob: oz.blob()
})
JSON Schema Customization
If Zod alone does not cover your JSON Schema requirements, you can extend or override the generated schema:
import { z } from 'zod'
import { oz } from '@orpc/zod'
const InputSchema = oz.openapi(
z.object({
name: z.string(),
}),
{
examples: [
{ name: 'Earth' },
{ name: 'Mars' },
],
// additional options...
}
)