Client Error Handling
oRPC supports several ways to handle client-side errors. In most cases, try/catch is enough. If you use Typesafe Errors, safe and createSafeClient let you handle them with full type inference.
Using try/catch
For most calls, use regular try/catch.
ts
try {
const data = await client.doSomething({ id: '123' })
}
catch (error) {
// handle error
}Using safe and isInferableError
When working with Typesafe Errors, use safe to preserve error type inference. It behaves like try/catch, but returns the typesafe result instead of throwing.
ts
import { isInferableError, safe } from '@orpc/client'
const exampleProcedure = os
.input(z.object({ id: z.string() }))
.errors({
RATE_LIMIT_EXCEEDED: {
data: z.object({ retryAfter: z.number() })
}
})
.handler(async ({ input, errors }) => {
throw errors.RATE_LIMIT_EXCEEDED({ data: { retryAfter: 1000 } })
})
// or { error, data, inferableError }
const [error, data, inferableError] = await safe(
call(exampleProcedure, { id: '123' })
)
if (isInferableError(error)) { // or inferableError
// handle inferable error
// or inferableError.data.retryAfter
console.log(error.data.retryAfter)
}
else if (error) {
// handle unknown error
}
else {
// handle success
console.log(data)
}INFO
safe supports both tuple and object forms:
[error, data, inferableError]{ error, data, inferableError }
inferableError is the same value as error when isInferableError(error) returns true; otherwise it is null.
Safe Client
If you use safe often, createSafeClient can reduce repetition by wrapping entire client calls with safe.
ts
import { createSafeClient } from '@orpc/client'
const safeClient = createSafeClient(client)
const [error, data] = await safeClient.doSomething({ id: '123' })
