tRPC vs oRPC vs ts-rest 2026: Choosing an End-to-End Type-Safe API Layer
If you build full-stack TypeScript apps, you have felt the specific pain these libraries exist to remove: the drift between what your server returns and what your client thinks it returns. REST with hand-written types rots the moment someone changes a response shape. OpenAPI code generation helps but adds a build step and a layer of generated noise. The end-to-end type-safe API libraries take a different route — you define a procedure once, and the client is fully typed from the same source, with no generation step and nothing to keep in sync.
tRPC is the library that made this pattern mainstream, and in 2026 it is still the incumbent: the largest ecosystem, the deepest TanStack Query integration, and the default for internal TypeScript apps. oRPC is the newer contender that keeps tRPC-style ergonomics but adds the things tRPC leaves to add-ons — first-class OpenAPI, Standard Schema support, native files and streaming. ts-rest takes the contract-first, REST-native path for teams who want typed HTTP rather than RPC. They look similar on a landing page and differ in ways that decide whether your API ages well. This guide makes the choice clear.
End-to-end type-safe API layers for TypeScript — tRPC, oRPC and ts-rest
This is the library-level comparison. If you are still deciding between the broader API *styles* — REST versus GraphQL versus the RPC approach itself — start with our guide to REST vs GraphQL vs tRPC. This article assumes you have chosen the type-safe RPC direction and are picking the tool.
The Three at a Glance
| Library | What it is | Core idea | Best for |
|---|---|---|---|
| tRPC | The original end-to-end type-safe RPC library | Define procedures, call them fully typed; no codegen | Internal TS apps, monorepos, Next.js/React where you own the client |
| oRPC | A newer RPC library with a real REST/OpenAPI surface | tRPC ergonomics + OpenAPI 3.1, Standard Schema, files, streaming | Public or mixed-client APIs that need an OpenAPI contract |
| ts-rest | A contract-first, REST-shaped type-safe layer | Describe REST endpoints in a shared contract; type both ends | Teams shipping REST that want it typed, non-TS consumers |
The mental model that keeps them straight: tRPC is RPC for your own app, oRPC is RPC plus a real REST surface, and ts-rest is typed REST. Everything below follows from that.
tRPC: The Incumbent
tRPC is the library that proved you do not need code generation to get a typed API. You write procedures on the server, grouped into routers; you export the router's *type* (not its code) to the client; and the client calls your procedures with full autocompletion and type checking. Nothing is generated, nothing is serialized into a schema file — the types flow through TypeScript's own inference.
// server: a tRPC router. The input is validated with Zod,
// and the return type is inferred automatically.
import { initTRPC } from "@trpc/server";
import { z } from "zod";
const t = initTRPC.create();
export const appRouter = t.router({
getUser: t.procedure
.input(z.object({ id: z.string() }))
.query(({ input }) => {
return { id: input.id, name: "Ada" };
}),
});
export type AppRouter = typeof appRouter;// client: fully typed from the exported AppRouter type.
// No codegen, no manually maintained response types.
const user = await trpc.getUser.query({ id: "123" });
// ^? { id: string; name: string }Two things keep tRPC the default in 2026. The first is the ecosystem: it has by far the largest adoption of the three — on the order of three million weekly npm downloads for the server package — which means the most boilerplates, the most Stack Overflow answers, the most middleware, and the most developers who already know it. The second is its TanStack Query integration, which is the deepest of any option here: tRPC procedures become React Query hooks with caching, request de-duplication, optimistic updates and invalidation essentially for free. For a React app, that pairing is a genuine productivity multiplier.
tRPC v11 went stable in March 2025 after a long pre-release, and it is a mature, actively maintained line — well into the 11.1x releases by late 2026, with no v12 yet. v11 also broadened schema support beyond Zod and lifted old type-depth limits that used to bite very large routers.
The trade-off: tRPC assumes you own both ends. It is RPC, not REST, so there are no stable URLs or HTTP verbs that an outside consumer can rely on, and OpenAPI output is an add-on — a separate community package rather than a core feature — that lags the core and forces your procedures into REST shapes. If your API will only ever be called by your own TypeScript client, none of that matters. If it will be consumed by a mobile team, a third party, or a non-TypeScript service, it matters a lot.
oRPC: tRPC Ergonomics With a Real REST Surface
oRPC starts from the same premise as tRPC — define a procedure, get a typed client, no codegen — and then systematically adds the things tRPC relegates to add-ons. It reached a stable 1.0 in late 2025 and has shipped actively since, so it is a real production option rather than an experiment.
The headline feature is OpenAPI. oRPC generates an OpenAPI 3.1 specification directly from your procedures as a core capability, not a bolt-on. That single fact changes what the API can be: the same definitions that give your TypeScript client its types also produce an accurate, always-current contract that Swagger UI, external developers and non-TypeScript clients can consume. You get RPC ergonomics internally and a real documented REST surface externally from one source.
// oRPC: define a procedure once. The TypeScript client is typed,
// and the same definition feeds an OpenAPI 3.1 document.
import { os } from "@orpc/server";
import { z } from "zod";
export const getUser = os
.input(z.object({ id: z.string() }))
.output(z.object({ id: z.string(), name: z.string() }))
.handler(async ({ input }) => {
return { id: input.id, name: "Ada" };
});Beyond OpenAPI, oRPC leans into interoperability. It supports the Standard Schema spec, so you can validate with Zod, Valibot or ArkType interchangeably rather than being tied to one library — useful if you care about bundle size and have already chosen Valibot, for example (see our Zod vs Yup vs Valibot comparison for why that choice matters). It handles file uploads, Blobs and streaming natively, which tRPC and ts-rest treat as rough edges. And its server actions are framework-agnostic, so you are not locked into one framework's implementation of the pattern.
The trade-off: oRPC's ecosystem is a fraction of tRPC's — fewer downloads, fewer ready-made boilerplates, fewer battle-tested examples in the wild — and it has had far less time in production. The library also publishes its own benchmarks claiming faster type-checking and lower memory than tRPC on very large projects; those may well be true, but they are vendor-authored, so verify them against your own codebase rather than taking them as settled. If you need its features, the trade is usually worth it; if you do not, tRPC's maturity is hard to argue with.
ts-rest: Contract-First, REST-Native Type Safety
ts-rest rejects the RPC framing entirely. Instead of defining procedures, you define a contract — a plain description of REST endpoints, their methods, paths, inputs and response shapes — and both the server and the client are typed against that single contract. The result is ordinary REST (real URLs, real HTTP verbs, real status codes) that happens to be fully type-safe end to end.
// ts-rest: a contract describes real REST endpoints.
// Server and client are both typed against it.
import { initContract } from "@ts-rest/core";
import { z } from "zod";
const c = initContract();
export const contract = c.router({
getUser: {
method: "GET",
path: "/users/:id",
responses: {
200: z.object({ id: z.string(), name: z.string() }),
},
},
});That contract-first, REST-shaped design is ts-rest's reason to exist. Because the API is REST underneath, it interoperates cleanly with non-TypeScript consumers, generating an OpenAPI document is natural, and you keep the familiar HTTP semantics that infrastructure, caching layers and other teams already understand. For a team that is going to ship REST regardless and simply wants to delete the class of bugs where the client and server disagree about a payload, ts-rest is the most direct fit.
The trade-off: ts-rest's development has visibly slowed — releases are less frequent than tRPC's or oRPC's — and its feature set is narrower: TanStack Query support is partial rather than deep, and it has no native file or streaming story. It is also the smallest community of the three. It remains a solid, focused tool for typed REST, but it is the most conservative choice and the one to double-check for active maintenance before you commit a long-lived product to it.
Developer Experience, OpenAPI and Ecosystem
A few dimensions decide most real choices:
Download figures quoted across comparison sites put tRPC's server package in the millions per week and oRPC and ts-rest in the few-hundred-thousand range — directionally useful for gauging ecosystem size, but worth checking on npm yourself since they move quickly.
How to Choose
Match the library to who calls your API, not to a feature checklist.
Choose tRPC if you are building an internal, TypeScript-only product — a Next.js app, a monorepo, a React client you own — and you want the safest, most-supported default with the deepest TanStack Query integration. The moment you say "the client is our own code," tRPC is the path of least resistance.
Choose oRPC if you want tRPC's ergonomics but also need a real OpenAPI 3.1 contract for third parties or non-TypeScript clients, want to validate with Valibot or ArkType rather than only Zod, or need native file and streaming support. It is the most feature-complete of the three and the right pick when your API has more than one kind of consumer.
Choose ts-rest if you are committed to REST — real endpoints, real verbs, outside consumers — and you simply want that REST to be type-safe through a shared contract. Confirm it is still actively maintained for your timeline, then enjoy the most conservative, REST-native option.
A quick heuristic: one TypeScript client points to tRPC, a public or mixed-client API with OpenAPI needs points to oRPC, and typed plain-REST points to ts-rest. And if your app is a single Next.js codebase with modest needs, remember that Server Actions may already cover you — reach for one of these libraries when you have a second client, a third party, or an API that must be documented.
The Bottom Line
Three end-to-end type-safe API libraries, three distinct bets about who consumes your API:
There is no universal winner — there is the right fit for who calls your API and how much reach beyond TypeScript you need. Decide that first, and the library chooses itself.
Starting a new API and want a proven foundation instead of a blank repo? Browse production-ready SaaS and backend starters on CodeCudos — every listing is quality-scored for TypeScript coverage, working flows and documentation — or, if you have built a polished tRPC or oRPC boilerplate, list it for sale. And if you are assembling the rest of the stack, our guides to the best tRPC templates and starters, Hono vs Express vs Fastify and TanStack Query vs SWR vs RTK Query cover what goes around the API layer.
