Documentation Index
Fetch the complete documentation index at: https://mintlify.com/middleapi/orpc/llms.txt
Use this file to discover all available pages before exploring further.
Install packages
Install the server and client packages. oRPC works with any schema validator — these examples use Zod.npm install @orpc/server @orpc/client zod
Define your router
Create your procedures and group them into a router. The os builder lets you chain .input(), .output(), and .handler() to define each operation.import type { IncomingHttpHeaders } from 'node:http'
import { ORPCError, os } from '@orpc/server'
import * as z from 'zod'
const PlanetSchema = z.object({
id: z.number().int().min(1),
name: z.string(),
description: z.string().optional(),
})
export const listPlanet = os
.input(
z.object({
limit: z.number().int().min(1).max(100).optional(),
cursor: z.number().int().min(0).default(0),
}),
)
.handler(async ({ input }) => {
// your list code here
return [{ id: 1, name: 'name' }]
})
export const findPlanet = os
.input(PlanetSchema.pick({ id: true }))
.handler(async ({ input }) => {
// your find code here
return { id: 1, name: 'name' }
})
export const createPlanet = os
.$context<{ headers: IncomingHttpHeaders }>()
.use(({ context, next }) => {
const user = parseJWT(context.headers.authorization?.split(' ')[1])
if (user) {
return next({ context: { user } })
}
throw new ORPCError('UNAUTHORIZED')
})
.input(PlanetSchema.omit({ id: true }))
.handler(async ({ input, context }) => {
// your create code here
return { id: 1, name: 'name' }
})
export const router = {
planet: {
list: listPlanet,
find: findPlanet,
create: createPlanet,
},
}
A router is a plain JavaScript object — no magic, no decorators. You can nest routers as deeply as you need.
Create your server
Pass your router to an RPCHandler and attach it to a Node.js HTTP server (or any supported runtime).import { createServer } from 'node:http'
import { RPCHandler } from '@orpc/server/node'
import { CORSPlugin } from '@orpc/server/plugins'
const handler = new RPCHandler(router, {
plugins: [new CORSPlugin()]
})
const server = createServer(async (req, res) => {
const result = await handler.handle(req, res, {
context: { headers: req.headers }
})
if (!result.matched) {
res.statusCode = 404
res.end('No procedure matched')
}
})
server.listen(
3000,
'127.0.0.1',
() => console.log('Listening on 127.0.0.1:3000')
)
oRPC supports Node.js, Cloudflare Workers, Deno, Bun, and any Fetch-compatible runtime. See the adapters section for all options. Create your client
The client is fully typed from your router — no code generation required.import type { RouterClient } from '@orpc/server'
import { createORPCClient } from '@orpc/client'
import { RPCLink } from '@orpc/client/fetch'
const link = new RPCLink({
url: 'http://127.0.0.1:3000',
headers: { Authorization: 'Bearer token' },
})
export const orpc: RouterClient<typeof router> = createORPCClient(link)
Call your API
Call procedures as if they were local async functions. TypeScript will infer the input and output types automatically.import { orpc } from './client'
const planets = await orpc.planet.list({ limit: 10 })
// ^? { id: number; name: string; description?: string }[]
Generate an OpenAPI spec (optional)
oRPC can produce a fully compliant OpenAPI specification from your router with no extra annotations.import { OpenAPIGenerator } from '@orpc/openapi'
import { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'
const generator = new OpenAPIGenerator({
schemaConverters: [new ZodToJsonSchemaConverter()]
})
const spec = await generator.generate(router, {
info: {
title: 'Planet API',
version: '1.0.0'
}
})
console.log(spec)
Next steps
Core concepts
Learn about procedures, routers, middleware, and context.
Server adapters
Deploy to Node.js, Cloudflare, Bun, Deno, and more.
Client setup
Connect from any frontend with full type safety.
OpenAPI
Generate OpenAPI specs and serve interactive API docs.