oRPC

Type-safe RPC for TypeScript. Define procedures on the server and call them from the client with full end-to-end type inference.

Integration

oRPC is a type-safe RPC library for TypeScript. It lets you define server procedures and call them from both server components and client components with full type safety.

Install

terminalbash
npm install @orpc/server @orpc/client

Define your router

router.tstypescript
import { os } from "@orpc/server";

export const router = {
  time: {
    getCurrent: os.handler(() => {
      return Date.now();
    })
  }
};

Create the client

client.tstypescript
import { createORPCClient } from '@orpc/client'
import { RPCLink } from '@orpc/client/fetch'
import type { RouterClient } from '@orpc/server'
import type { router } from './router'

const baseURL =
  typeof window !== 'undefined'
    ? `${window.location.origin}/rpc`
    : process.env.RPC_URL || 'http://localhost:3000/rpc'

const link = new RPCLink({ url: baseURL })

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

Register the RPC handler

server/rpc/[...rest].tstypescript
import { RPCHandler } from '@orpc/server/node'
import { onError } from '@orpc/server'
import { router } from '../../router'

const handler = new RPCHandler(router, {
  interceptors: [
    onError((error) => {
      console.error(error)
    })
  ]
})

async function handleRequest(req: any, res: any) {
  const { matched } = await handler.handle(req, res, {
    prefix: '/rpc',
    context: {}
  })

  if (matched) return

  res.json({ error: 404 })
}

export const HEAD = handleRequest
export const GET = handleRequest
export const POST = handleRequest
export const PUT = handleRequest
export const PATCH = handleRequest
export const DELETE = handleRequest

Call procedures from a server page

app/pages/index.tsxtypescript
import { useHtml } from "nukejs"
import { orpc } from "../../client"

export default async function Index() {
  useHtml({ title: "Home" })

  const currentTime = await orpc.time.getCurrent()

  return (
    <main>
      <h1>Server time</h1>
      <p>{currentTime}</p>
    </main>
  )
}

Call procedures from a client component

app/components/TimeDisplay.tsxtypescript
"use client"

import { useState } from "react"
import { orpc } from "../../client"

export default function TimeDisplay() {
  const [time, setTime] = useState<number | null>(null)

  async function refresh() {
    setTime(await orpc.time.getCurrent())
  }

  return (
    <div>
      <p>{time ?? "—"}</p>
      <button onClick={refresh}>Refresh</button>
    </div>
  )
}
💡
Type inference is automaticThe client type is inferred from the router, so procedure arguments and return values are fully typed across the client and server without generating any code.