Skip to main content
Creates a fully type-safe client-side client from a ClientLink.
import type { RouterClient } from '@orpc/server'
import { createORPCClient } from '@orpc/client'
import { RPCLink } from '@orpc/client/fetch'

const link = new RPCLink({
  url: 'https://api.example.com',
})

export const orpc: RouterClient<typeof router> = createORPCClient(link)

// Call procedures:
const planets = await orpc.planet.list({ limit: 10 })

Options

path
readonly string[]
Base path for all calls. Useful for calling a subset of the router.
const planetClient = createORPCClient(link, { path: ['planet'] })
await planetClient.list({ limit: 5 })

createSafeClient(client)

Wraps every procedure call with the safe() utility. Instead of throwing, it returns { data, error, isDefined } tuples.
import { createSafeClient } from '@orpc/client'

const safeClient = createSafeClient(orpc)

const { data, error, isDefined } = await safeClient.planet.find({ id: 1 })

if (error) {
  if (isDefined) {
    // typed ORPCError
    console.log(error.code, error.data)
  } else {
    // undefined/unknown error
  }
} else {
  console.log(data)
}

The standard link for calling oRPC servers over HTTP.
import { RPCLink } from '@orpc/client/fetch'  // for Fetch, Edge, and Node.js 18+

Constructor options

new RPCLink({
  url: 'https://api.example.com',
  headers: {
    Authorization: 'Bearer token',
  },
  fetch: customFetch,  // optional custom fetch implementation
})
url
string
required
The base URL of your oRPC server.
headers
Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>)
Static headers or an async function that returns headers per request.
headers: async () => ({
  Authorization: `Bearer ${await getToken()}`,
})
fetch
typeof fetch
Custom fetch implementation. Defaults to the global fetch.
plugins
ClientPlugin[]
Array of client-side plugins for request/response transformation.

A link that dynamically resolves to another link based on the request path, input, and context. Useful for routing to different backends or switching between environments.
import { DynamicLink } from '@orpc/client'
import { RPCLink } from '@orpc/client/fetch'

const dynamicLink = new DynamicLink(async (options, path, input) => {
  if (path[0] === 'admin') {
    return new RPCLink({ url: 'https://admin.api.example.com' })
  }
  return new RPCLink({ url: 'https://api.example.com' })
})

const orpc = createORPCClient(dynamicLink)

Resolver signature

new DynamicLink<TClientContext>(
  resolver: (
    options: ClientOptions<TClientContext>,
    path: readonly string[],
    input: unknown,
  ) => Promisable<ClientLink<TClientContext>>
)

ORPCError

The error class used throughout oRPC. Thrown by servers and received by clients.
import { ORPCError } from '@orpc/client'

Properties

PropertyTypeDescription
codestringError code (e.g. 'NOT_FOUND', 'UNAUTHORIZED')
statusnumberHTTP status code
messagestringHuman-readable message
dataTDataOptional typed payload attached to the error
definedbooleantrue if the error was declared in an error map
causeunknownUnderlying cause (standard ErrorOptions)

toJSON()

Serializes the error to a plain object for transport:
error.toJSON()
// => { defined, code, status, message, data }

Common error codes

CodeStatus
BAD_REQUEST400
UNAUTHORIZED401
FORBIDDEN403
NOT_FOUND404
CONFLICT409
UNPROCESSABLE_CONTENT422
TOO_MANY_REQUESTS429
INTERNAL_SERVER_ERROR500

isDefinedError(error)

Narrowing helper. Returns true if error is an ORPCError with defined === true (i.e., declared in an error map).
import { isDefinedError } from '@orpc/client'

try {
  await orpc.planet.find({ id: 999 })
} catch (err) {
  if (isDefinedError(err)) {
    // err is ORPCError<any, any> with .defined === true
    console.log(err.code)  // 'NOT_FOUND'
  }
}

safe(promise)

Wraps a promise and returns a [error, data, isDefined] tuple instead of throwing.
import { safe } from '@orpc/client'

const [error, data] = await safe(orpc.planet.find({ id: 1 }))

if (error) {
  console.log('Error:', error.message)
} else {
  console.log('Planet:', data)
}

Return type

A 4-tuple (also accessible as named properties):
type SafeResult<TOutput, TError> =
  // Success
  | [error: null, data: TOutput, isDefined: false, isSuccess: true]
  // Undefined (untyped) error
  | [error: Exclude<TError, ORPCError<any, any>>, data: undefined, isDefined: false, isSuccess: false]
  // Defined (typed) ORPCError
  | [error: Extract<TError, ORPCError<any, any>>, data: undefined, isDefined: true, isSuccess: false]
Each element is also available as a named property: result.error, result.data, result.isDefined, result.isSuccess.

Client plugins

Client plugins implement the StandardLinkPlugin<T> interface from @orpc/client/standard. Built-in plugins are available from @orpc/client/plugins:
import {
  ClientRetryPlugin,
  BatchLinkPlugin,
  DedupeRequestsPlugin,
  RetryAfterPlugin,
  SimpleCsrfProtectionLinkPlugin,
} from '@orpc/client/plugins'
Pass plugins to RPCLink:
import { RPCLink } from '@orpc/client/fetch'
import { ClientRetryPlugin } from '@orpc/client/plugins'

const link = new RPCLink({
  url: 'https://api.example.com',
  plugins: [new ClientRetryPlugin({ default: { retry: 3 } })],
})

Build docs developers (and LLMs) love