Blog
October 6, 20264 min readIbrahim Elkamali

tRPC vs oRPC: choose by client reach and contract needs

Compare tRPC and oRPC by typing model, client frameworks, OpenAPI needs, and deployment fit, with the Better Fullstack command that generates each.

trpcorpcapi
Markdown

Hono API stack with typed contracts, Better Auth, and PostgreSQL

tRPC and oRPC solve the same first problem: a TypeScript client calls server procedures with inferred types and no code generation step. Both projects describe that goal in their own documentation (tRPC, oRPC).

The differences appear at the edges of the API. Which clients call it? Does anything outside your TypeScript code need to call it? Should the contract exist before the implementation? Answer those three questions and the choice usually follows.

The shared ground

Both libraries give you:

  • procedures written as TypeScript functions on the server;
  • runtime input validation with schema libraries such as Zod, Valibot, and ArkType (tRPC validators, oRPC overview);
  • client types inferred from the server router;
  • a TanStack Query integration.

Neither one replaces authorization checks inside procedures, input size limits, or error responses that a client can act on. Those stay your job with either library.

Typing model and contracts

With tRPC, the router type is the contract. The server exports AppRouter, the client imports that type, and changes flow through TypeScript inference. This works well when server and client live in one repository and ship together.

oRPC uses the same inference by default. It also supports a contract-first style: the @orpc/contract package describes input, output, errors, and metadata without business logic, and the implementation must match it. That matters when a contract is reviewed before anyone writes a handler, or when separate teams own each side.

Who calls the API

This is usually the deciding criterion.

tRPC's official client integrations center on React, through its TanStack React Query integration and Next.js. For a frontend without an official integration, the tRPC docs point to the framework-agnostic vanilla client.

oRPC can also serve the same router as a REST API with OpenAPI metadata. Procedures without explicit routing metadata are exposed as POST endpoints whose paths follow the router structure. If partners, scripts, or clients written in other languages need the API, that is a concrete reason to start with oRPC. tRPC's documentation centers on its own client protocol, so confirm how you would produce an OpenAPI description before choosing it for that case.

Runtime and deployment

Both mount inside the server you already run. oRPC documents adapters for Node.js, Bun, Deno, and Cloudflare Workers. tRPC can run standalone or be mounted as an endpoint on an existing server through its adapters. Pick the backend and runtime first, then check the adapter for that exact host.

What Better Fullstack generates

The two commands below differ only in --api:

bash
bun create better-fullstack@latest my-app \
  --ecosystem typescript \
  --frontend tanstack-router \
  --backend hono \
  --runtime bun \
  --database sqlite \
  --orm drizzle \
  --auth better-auth \
  --api trpc
bash
bun create better-fullstack@latest my-app \
  --ecosystem typescript \
  --frontend tanstack-router \
  --backend hono \
  --runtime bun \
  --database sqlite \
  --orm drizzle \
  --auth better-auth \
  --api orpc

Add --dry-run to preview either project without writing files. The generated projects differ in these places:

Concern--api trpc--api orpc
Procedure builderinitTRPC in packages/api/src/index.tsos from @orpc/server in packages/api/src/index.ts
Auth guardprotectedProcedure throws TRPCError UNAUTHORIZEDprotectedProcedure throws ORPCError("UNAUTHORIZED")
Hono mount@hono/trpc-server on /trpc/*RPCHandler on /rpc, plus OpenAPIHandler on /api-reference
API referencenoneOpenAPIReferencePlugin with the Zod JSON Schema converter
Web clienthttpBatchLink with createTRPCOptionsProxyRPCLink with createTanstackQueryUtils

Better Fullstack also enforces the client reach difference. tRPC is offered only with React-based frontends. With Svelte, Nuxt, Solid, or SolidStart, oRPC is the option of the two that the CLI accepts; --frontend svelte --api trpc is rejected with a suggestion to use oRPC. This command generates a SvelteKit client that uses @orpc/tanstack-query with @tanstack/svelte-query:

bash
bun create better-fullstack@latest my-app \
  --ecosystem typescript \
  --frontend svelte \
  --backend hono \
  --runtime bun \
  --database sqlite \
  --orm drizzle \
  --auth better-auth \
  --api orpc

These are generated projects, not runtime-verified deployments. Install, build, and exercise the slice you care about before committing to either.

Decision table

If this is the stronger constraintStart with
React client in the same repository, nothing elseEither; the one your team already knows
Svelte, Nuxt, Solid, or SolidStart clientoRPC
OpenAPI description from the same routeroRPC
Contract reviewed before implementationoRPC with @orpc/contract
Existing tRPC routers in productiontRPC, unless a requirement above rules it out

Compare real generated stacks

The Hono tRPC Drizzle guide and Hono Better Auth guide show both layers in a full project. If the API mostly serves one web app, read self backend vs separate API before adding a separate server at all.

GitHub Sponsors