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.
os — the global builder
os is the root Builder instance. All procedure and router definitions start here.
import { os } from '@orpc/server'
Builder methods
.$context<TContext>()
Sets the expected initial context type for all procedures. Call this first when you want type-safe context.
const authed = os.$context<{ userId: string }>()
const getProfile = authed
.handler(async ({ context }) => {
return { id: context.userId }
})
.$config(config)
Overrides builder-level configuration.
os.$config({
initialInputValidationIndex: 0,
initialOutputValidationIndex: 0,
dedupeLeadingMiddlewares: true,
})
Sets the initial metadata type for procedures.
.$route(initialRoute)
Sets the initial route definition (useful as a router-level default).
Sets the initial input schema. Procedures inherit this schema.
.use(middleware)
Adds middleware to the pipeline. Middleware runs for every procedure defined after this call.
const auth = os.use(async ({ context, next }) => {
const user = await authenticate(context.headers)
return next({ context: { user } })
})
.errors(errorMap)
Registers type-safe errors. Error constructors are available in procedure handlers via errors.*.
const withErrors = os.errors({
NOT_FOUND: { status: 404, message: 'Not found' },
UNAUTHORIZED: { status: 401 },
})
Sets or merges metadata onto subsequent procedures.
.route(route)
Sets or merges route options (method, path, tags, etc.) onto subsequent procedures.
os.route({ method: 'GET', path: '/planets', tags: ['planets'] })
Defines the input validation schema for a procedure.
import * as z from 'zod'
const proc = os.input(z.object({ name: z.string() }))
.output(schema)
Defines the output validation schema for a procedure.
.handler(fn)
Defines the procedure handler. Returns a DecoratedProcedure.
const listPlanets = os
.input(z.object({ limit: z.number().optional() }))
.handler(async ({ input, context, errors }) => {
return [{ id: 1, name: 'Earth' }]
})
.prefix(path)
Prefixes all procedures in a router with the given path. Only affects procedures with explicit .route({ path: ... }).
const apiRouter = os.prefix('/api').router({ ... })
Adds OpenAPI tags to all procedures in the subsequent router.
.router(router)
Applies all accumulated options (middleware, errors, prefix, tags) to a router.
export const router = os
.use(authMiddleware)
.errors({ UNAUTHORIZED: {} })
.router({
planet: { list: listPlanets, create: createPlanet },
})
.lazy(loader)
Creates a lazy-loaded router. The loader is called only when the route is first matched.
export const router = os.lazy(() => import('./planet-router'))
.middleware(fn)
Creates a DecoratedMiddleware (useful for reuse and composition).
const logMiddleware = os.middleware(async ({ next, path }) => {
console.log('Calling', path)
return next()
})
ORPCError
Thrown inside procedure handlers or middleware to signal known errors.
import { ORPCError } from '@orpc/server'
throw new ORPCError('NOT_FOUND', {
message: 'Planet not found',
data: { id: 42 },
})
// Or use a shorthand from the errors constructor map:
throw errors.NOT_FOUND({ message: 'Custom message' })
Properties
| Property | Type | Description |
|---|
code | string | The error code (e.g. 'NOT_FOUND') |
status | number | HTTP status code |
message | string | Human-readable message |
data | TData | Optional typed payload |
defined | boolean | true if registered in an error map |
cause | unknown | Original cause (from ErrorOptions) |
Common error codes
BAD_REQUEST (400), UNAUTHORIZED (401), FORBIDDEN (403), NOT_FOUND (404), CONFLICT (409), UNPROCESSABLE_CONTENT (422), INTERNAL_SERVER_ERROR (500)
Types
RouterClient<TRouter, TClientContext>
Infers a fully type-safe callable client type from a router. Nested procedures become nested async functions.
import type { RouterClient } from '@orpc/server'
const client: RouterClient<typeof router> = createRouterClient(router, {
context: { headers: req.headers },
})
const planets = await client.planet.list({ limit: 10 })
Infers the input types of all procedures in a router.
import type { InferRouterInputs } from '@orpc/server'
type Inputs = InferRouterInputs<typeof router>
// => { planet: { list: { limit?: number }, create: { name: string } } }
InferRouterOutputs<TRouter>
Infers the output types of all procedures in a router.
createRouterClient()
Creates a server-side client for calling procedures directly (without HTTP).
import { createRouterClient } from '@orpc/server'
const client = createRouterClient(router, {
context: { userId: '123' },
})
const result = await client.planet.list({ limit: 5 })
lazy()
Wraps a dynamic import in a lazy router for improved cold start times.
import { lazy } from '@orpc/server'
export const router = {
planet: lazy(() => import('./planet-router')),
}
EventPublisher
Publishes events to multiple async iterator subscribers. Used for SSE/streaming.
import { EventPublisher } from '@orpc/server'
// Generic type maps event names to their payload shapes
const publisher = new EventPublisher<{
updated: { id: number; name: string }
deleted: { id: number }
}>()
// Publish an event (synchronous, returns void)
publisher.publish('updated', { id: 1, name: 'Earth' })
// Subscribe via async iterator (for use in a procedure handler)
async function* streamUpdates(signal: AbortSignal) {
yield* publisher.subscribe('updated', { signal })
}
// Subscribe via callback
const unsubscribe = publisher.subscribe('updated', (payload) => {
console.log('Planet updated:', payload)
})
unsubscribe() // remove listener
eventIterator()
Used in output schemas to declare that a procedure returns an event iterator (async iterable).
import { eventIterator } from '@orpc/server'
import * as z from 'zod'
const stream = os
.output(eventIterator(z.object({ type: z.string() })))
.handler(async function* ({ input }) {
yield { type: 'start' }
yield { type: 'done' }
})
isDefinedError() and safe()
import { isDefinedError, safe } from '@orpc/server'
// Check if an error is a known (registered) ORPCError
if (isDefinedError(error)) {
console.log(error.code, error.data)
}
// Wrap a procedure call to get [error, data] tuple instead of throwing
const [err, result] = await safe(client.planet.find({ id: 1 }))
Lifecycle hooks
Hooks for observing procedure execution:
import { onStart, onSuccess, onError, onFinish } from '@orpc/server'
const telemetryMiddleware = os.middleware(
onFinish((result, { path }) => {
console.log('Finished', path, result.success)
}),
)
| Hook | Called when |
|---|
onStart(fn) | Before the handler runs |
onSuccess(fn) | Handler completes successfully |
onError(fn) | Handler throws an error |
onFinish(fn) | Always, after success or error |
RPCHandler adapters
Handlers for different runtimes. All support plugins.
import { RPCHandler } from '@orpc/server/fetch'
const handler = new RPCHandler(router, { plugins: [] })
export default {
fetch(request: Request) {
return handler.handle(request, { context: {} })
},
}
import { RPCHandler } from '@orpc/server/node'
const handler = new RPCHandler(router)
createServer(async (req, res) => {
const result = await handler.handle(req, res, {
context: { headers: req.headers },
})
if (!result.matched) res.end('Not found')
}).listen(3000)
import { RPCHandler } from '@orpc/server/aws-lambda'
const handler = new RPCHandler(router)
// Requires Lambda Response Streaming (awslambda.streamifyResponse)
export const main = awslambda.streamifyResponse(
async (event, responseStream) => {
const result = await handler.handle(event, responseStream, {
context: {},
})
if (!result.matched) {
const res = awslambda.HttpResponseStream.from(responseStream, { statusCode: 404 })
res.end('Not found')
}
}
)
Plugins
CORSPlugin
import { CORSPlugin } from '@orpc/server/plugins'
new RPCHandler(router, {
plugins: [
new CORSPlugin({
origin: ['https://app.example.com'],
allowHeaders: ['Authorization', 'Content-Type'],
}),
],
})
BatchHandlerPlugin
Enables batching multiple procedure calls into a single HTTP request.
import { BatchHandlerPlugin } from '@orpc/server/plugins'
new RPCHandler(router, {
plugins: [new BatchHandlerPlugin()],
})
SimpleCsrfProtectionHandlerPlugin
Validates a custom header to prevent CSRF attacks.
import { SimpleCsrfProtectionHandlerPlugin } from '@orpc/server/plugins'
new RPCHandler(router, {
plugins: [new SimpleCsrfProtectionHandlerPlugin()],
})
Allows procedures to set response headers via context.
import { ResponseHeadersPlugin } from '@orpc/server/plugins'
new RPCHandler(router, {
plugins: [new ResponseHeadersPlugin()],
})