Getting Started
oRPC (OpenAPI Remote Procedure Call) combines RPC (Remote Procedure Call) with OpenAPI, allowing you to define and call remote (or local) procedures through a type-safe API while adhering to the OpenAPI specification.
oRPC simplifies RPC service definition, making it easy to build scalable applications—from simple scripts to complex microservices.
This guide covers the basics: defining procedures, handling errors, and integrating with popular frameworks.
Prerequisites
- Node.js 18+ (20+ recommended) | Bun | Deno | Cloudflare Workers
- A package manager: npm | pnpm | yarn | bun | deno
- A TypeScript project (strict mode recommended)
Installation
npm install @orpc/server@latest @orpc/client@latest
yarn add @orpc/server@latest @orpc/client@latest
pnpm add @orpc/server@latest @orpc/client@latest
bun add @orpc/server@latest @orpc/client@latest
deno install npm:@orpc/server@latest npm:@orpc/client@latest
Define App Router
We'll use Zod for schema validation (optional, any standard schema is supported).
import type { IncomingHttpHeaders } from 'node:http'
import { ORPCError, os } from '@orpc/server'
import { 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
}
}
Create Server
Using Node.js as the server runtime, but oRPC also supports other runtimes like Bun, Deno, Cloudflare Workers, ...
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')
)
Learn more about RPCHandler.
Create Client
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)
Supports both client-side clients and server-side clients.
Call Procedure
End-to-end type-safety and auto-completion out of the box.
const planet = await orpc.planet.find({ id: 1 })
orpc.planet.create
Next Steps
This guide introduced the RPC aspects of oRPC. To explore OpenAPI integration, visit the OpenAPI Guide.