Skip to content
You are reading the v2 docs, currently in beta.V1 docs
oRPC
Esc
↑↓navigate↵open⌘Jpreview
On this page

OpenRPC Specification

Generate OpenRPC documents from oRPC contracts and routers with OpenRPCGenerator and openrpc metadata, and serve them with service discovery.

OpenRPC Generator

OpenRPCGenerator describes a contract or router as an OpenRPC 1.3 document, with JSON Schemas from your schema converters:

import { OpenRPCGenerator } from '@orpc/openrpc'
import { ZodToJsonSchemaConverter } from '@orpc/zod'

const generator = new OpenRPCGenerator({
  converters: [new ZodToJsonSchemaConverter()],
})

const spec = await generator.generate(router, {
  base: {
    info: { title: 'Planets', version: '1.0.0' },
    servers: [{ url: 'https://example.com/rpc' }],
  },
  filter: (procedure, path) => !path.includes('internal'),
})

Each procedure becomes a method, in the same way OpenRPC Handler maps them:

  • Params: an object input is described as one by-name param per property, any other input as a single by-position input param, and a method without input has no params.
  • Result: the output schema, which also allows null when the output is optional.
  • Errors: the typesafe errors with their JSON-RPC codes. Pass the handler’s errorCodeMap to the generator too.
  • Metadata: from openrpc metadata.

Procedures with an event iterator are skipped, as the handler does not serve them. Reusable schemas, such as Zod schemas with .meta({ id: 'Planet' }), are placed in components.schemas.

Metadata

Use openrpc metadata to describe a procedure’s method. Fields left undefined fall back to its openapi metadata, so procedures already documented for OpenAPI need no extra metadata.

import { openrpc } from '@orpc/openrpc'

const findPlanet = os
  .meta(openrpc({
    summary: 'Find a planet',
    description: 'Returns a single planet.',
    tags: ['planets'],
    deprecated: false,
    externalDocs: { url: 'https://example.com/planets' },
    examples: [
      { name: 'Earth', input: { id: 1 }, output: { id: 1, name: 'Earth' } },
    ],
  }))
  .input(z.object({ id: z.number() }))
  .handler(({ input }) => getPlanet(input.id))

Example inputs are described as params the same way the method’s params are, here [{ "name": "id", "value": 1 }]. As on the wire, undefined params are left out and an undefined output is shown as null.

Use spec to customize the generated Method Object: an object replaces it, and a function receives and returns it:

const findPlanet = os
  .meta(openrpc({
    spec: method => ({ ...method, 'x-rate-limit': 100 }),
  }))

When openrpc is applied multiple times, fields are overridden by the most recent call, except tags, which are concatenated, and spec, which is combined: two functions are chained, a function combined with an object is applied to that object, and between two objects the most recent one wins.

Service Discovery

The discovery option of OpenRPC Handler answers the rpc.discover method with the document, as the OpenRPC Service Discovery method, and also serves it over GET at /openrpc.json, relative to the prefix:

const handler = new OpenRPCHandler(router, {
  discovery: {
    spec: () => generator.generate(router), // memoize it for production
    specPath: '/openrpc.json', // or false to disable the GET endpoint
  },
})

Throw an ORPCError from spec to refuse the document, such as to callers who are not signed in.

Tools such as the OpenRPC Playground can then load the document from http://localhost:3000/rpc/openrpc.json.

Last updated on

Was this page helpful?