Skip to content

Client-Side Clients

Client-side clients call procedures remotely, in a different process or on a different machine. They are useful in frontend applications, mobile apps, or any setup where the client and server run in different environments.

Installation

sh
npm install @orpc/client@beta
sh
yarn add @orpc/client@beta
sh
pnpm add @orpc/client@beta
sh
bun add @orpc/client@beta
sh
deno add npm:@orpc/client@beta

Creating a Client

To create a client, first set up a link that defines how the client communicates with the server. This can be an RPC Link, an OpenAPI Link, or any custom link. Then create a client for your router or contract using createORPCClient.

ts
import { createORPCClient } from '@orpc/client'
import { RouterContractClient } from '@orpc/contract'
import { RouterClient } from '@orpc/server'

// if you are following contract-first approach
const contractClient: RouterContractClient<typeof contract> = createORPCClient(link)

// if you are following normal approach
const normalClient: RouterClient<typeof router> = createORPCClient(link)

TIP

You can export RouterClient<typeof router> or RouterContractClient<typeof contract> from the server to avoid importing the contract or router in the client.

Calling Procedures

Once your client is set up, you can call your procedures as if they were local functions.

ts
const 
pong
= await
client
.
ping
()
client
.
ping

Client Context

Client context lets you pass values with each call, such as auth tokens or cache hints.

ts
interface ClientContext {
  token?: string
}

// if you are following contract-first approach
const client: RouterContractClient<typeof contract, ClientContext> = createORPCClient(link)

// if you are following normal approach
const client: RouterClient<typeof router, ClientContext> = createORPCClient(link)

const output = await client.someProcedure(input, {
  context: {
    token: 'abc123',
  },
})

Interceptors

Interceptors let you wrap client calls. They are similar to interceptors in links, but are more typesafe because the exact input, output, and error types of each client are known. You can provide per-client interceptors with scoped.

ts
import { isInferableError, safe } from '@orpc/client'

const client: RouterClient<typeof router, ClientContext> = createORPCClient(link, {
  interceptors: [
    async ({ context, path, next }) => {
      const [error, data] = await safe(next())

      if (error) {
        if (isInferableError(error)) {
          // handle typesafe errors
        }

        throw error
      }

      return data
    }
  ],
  scoped: {
    planet: {
      find: {
        interceptors: [ // <- these interceptors only apply to client.planet.find
          async ({ context, path, next }) => {
            return next()
          }
        ]
      }
    }
  }
})

INFO

You can use safe and isInferableError together for typesafe error handling in interceptors.

Merging Clients

In oRPC, a client is just an object-like structure. To merge multiple clients, assign each client to a property on a new object:

ts
const clientA: RouterClient<typeof routerA> = createORPCClient(linkA)
const clientB: RouterClient<typeof routerB> = createORPCClient(linkB)
const clientC: RouterClient<typeof routerC> = createORPCClient(linkC)

export const orpc = {
  a: clientA,
  b: clientB,
  c: clientC,
}

Utilities

INFO

These utilities can also be used for server-side clients and are not specific to client-side clients.

Infer Client Inputs

Infers input types for each procedure in a client.

ts
import type { InferClientInputs } from '@orpc/client'

type Inputs = InferClientInputs<typeof client>

type FindPlanetInput = Inputs['planet']['find']

Infer Client Body Inputs

Infers body input types for each procedure in a client. If an endpoint's input includes { body: ... }, only the body portion is extracted. Otherwise, the entire input type is used.

ts
import type { InferClientBodyInputs } from '@orpc/client'

type BodyInputs = InferClientBodyInputs<typeof client>

type FindPlanetBodyInput = BodyInputs['planet']['find']

Infer Client Outputs

Infers output types for each procedure in a client.

ts
import type { InferClientOutputs } from '@orpc/client'

type Outputs = InferClientOutputs<typeof client>

type FindPlanetOutput = Outputs['planet']['find']

Infer Client Body Outputs

Infers body output types for each procedure in a client. If an endpoint's output includes { body: ... }, only the body portion is extracted. Otherwise, the entire output type is used.

ts
import type { InferClientBodyOutputs } from '@orpc/client'

type BodyOutputs = InferClientBodyOutputs<typeof client>

type FindPlanetBodyOutput = BodyOutputs['planet']['find']

Infer Client Errors

Infers the errors each procedure in a client can throw when using type-safe error handling.

ts
import type { InferClientErrors } from '@orpc/client'

type Errors = InferClientErrors<typeof client>

type FindPlanetError = Errors['planet']['find']

Infer Client Error

Infers all possible errors the entire client can throw. This is useful with type-safe error handling.

ts
import type { InferClientError } from '@orpc/client'

type ClientError = InferClientError<typeof client>

Infer Client Context

Infers the client context type from a client.

ts
import type { InferClientContext } from '@orpc/client'

type Context = InferClientContext<typeof client>

Released under the MIT License.