Skip to main content
oRPC provides several mechanisms to customize how procedures appear in the generated OpenAPI spec.

Per-procedure route metadata

The .route() method on a procedure lets you set OpenAPI-relevant metadata:
import { os } from '@orpc/server'
import * as z from 'zod'

const createPlanet = os
  .route({
    method: 'POST',
    path: '/planets',
    summary: 'Create a planet',
    description: 'Adds a new planet to the database.',
    tags: ['planets'],
    deprecated: false,
    successStatus: 201,
    successDescription: 'Planet created successfully',
    operationId: 'createPlanet',
  })
  .input(z.object({ name: z.string() }))
  .output(z.object({ id: z.number(), name: z.string() }))
  .handler(async ({ input }) => ({ id: 1, ...input }))

Available route fields

FieldTypeDescription
method'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'HTTP method. Defaults to POST.
pathstringURL path. Supports {param} syntax for dynamic segments.
summarystringShort operation summary.
descriptionstringLonger operation description.
tagsstring[]Tags for grouping in the UI.
deprecatedbooleanMarks the operation as deprecated.
successStatusnumberSuccess HTTP status code. Defaults to 200.
successDescriptionstringDescription for the success response.
operationIdstringUnique operation ID. Defaults to the procedure path (e.g. planet.create).

oo.spec() — full operation override

For complete control over the OpenAPI operation object, use oo.spec(). This lets you annotate middleware or error map entries with operation-level overrides that are applied during spec generation.
import { os } from '@orpc/server'
import { oo } from '@orpc/openapi'
import * as z from 'zod'

const requiresAuth = oo.spec(
  os.middleware(async ({ context, next }) => {
    // authentication logic
    return next()
  }),
  {
    // Extend all operations that use this middleware
    security: [{ bearerAuth: [] }],
  },
)

const createPlanet = os
  .use(requiresAuth)
  .input(z.object({ name: z.string() }))
  .handler(async ({ input }) => ({ id: 1, ...input }))
oo.spec() can accept either a partial OperationObject or a function that receives and returns the current operation:
import { oo } from '@orpc/openapi'

const myMiddleware = oo.spec(
  os.middleware(async ({ next }) => next()),
  (currentOperation, procedure) => ({
    ...currentOperation,
    // Add a custom extension
    'x-internal': false,
    security: [{ apiKey: [] }],
  }),
)
oo.spec() works on middleware and error map items. When a procedure uses multiple annotated middleware, the overrides are applied in order from first to last.

Setting the full operation spec directly

For complete bypass of oRPC’s spec generation logic for a given procedure, set spec directly in .route():
const myProcedure = os
  .route({
    method: 'GET',
    path: '/status',
    spec: {
      operationId: 'getStatus',
      summary: 'Health check',
      responses: {
        200: { description: 'OK' },
      },
    },
  })
  .handler(async () => ({ status: 'ok' }))
You can also provide spec as a function that receives the auto-generated operation and returns a modified one:
.route({
  spec: (generated) => ({
    ...generated,
    'x-custom-field': true,
  }),
})

Tagging all procedures in a sub-router

Use .tag() on a builder to apply tags to an entire group of procedures:
import { os } from '@orpc/server'

const planetRouter = os.tag('planets').router({
  list: os.handler(async () => []),
  create: os.handler(async () => ({ id: 1 })),
})
// Both 'list' and 'create' will have the 'planets' tag in the spec

Common schemas ($ref sharing)

The commonSchemas option in generator.generate() registers frequently-used schemas under #/components/schemas, replacing inline duplicates with $ref:
import * as z from 'zod'

const PlanetSchema = z.object({ id: z.number(), name: z.string() })

const spec = await generator.generate(router, {
  info: { title: 'API', version: '1.0.0' },
  commonSchemas: {
    Planet: { schema: PlanetSchema },
    // Register the undefined-error shape as a named component
    UndefinedError: { error: 'UndefinedError' },
  },
})

Custom error response body schema

By default, oRPC generates error response bodies using its standard shape. Override this globally with customErrorResponseBodySchema:
const spec = await generator.generate(router, {
  info: { title: 'API', version: '1.0.0' },
  customErrorResponseBodySchema: (definedErrors, status) => ({
    type: 'object',
    properties: {
      success: { const: false },
      error: {
        type: 'object',
        properties: {
          code: { type: 'string' },
          message: { type: 'string' },
        },
        required: ['code', 'message'],
      },
    },
    required: ['success', 'error'],
  }),
})
Return null or undefined to use oRPC’s default error body for a given status.

Filtering procedures from the spec

Use the filter option to exclude procedures from the spec entirely:
const spec = await generator.generate(router, {
  info: { title: 'Public API', version: '1.0.0' },
  filter: ({ path, contract }) => {
    // Exclude internal/admin procedures
    return path[0] !== 'admin' && path[0] !== 'internal'
  },
})

Build docs developers (and LLMs) love