tRPC, GraphQL, and REST solve the same problem—getting typed data from a server to a client—with different tradeoffs. tRPC gives end-to-end TypeScript type safety with no code generation step, but only works within a TypeScript codebase. GraphQL lets clients request exactly the fields they need, at the cost of a steeper setup and learning curve. REST stays the most portable and language-agnostic option, at the cost of over- or under-fetching data by default. The right choice depends on the team and the API’s audience, not on which technology is objectively "best."
| Criteria | tRPC | GraphQL | REST |
|---|---|---|---|
| Type safety | End-to-end, no codegen | Schema-based, optional | Manual |
| Over/under-fetching | Minimal | Minimal (client picks fields) | Common |
| Language support | TypeScript only | Language-agnostic | Language-agnostic |
| Tooling & DX | Strong in TS + Zod | Built-in introspection | Mature OpenAPI/Swagger ecosystem |
| Best fit | Internal TS monorepos | Complex, multi-source frontends | Public, multi-language APIs |
Use it when: Internal APIs inside a single TypeScript codebase or monorepo—especially Next.js-style full-stack projects where the same team owns client and server.
Unmatched type safety and developer speed in a TypeScript-only environment, with near-zero manual typing and fewer type-mismatch bugs. The tradeoff: it only works in TypeScript, so it is not suitable for public APIs or polyglot backends.
Use it when: Complex, fluid frontends or products aggregating data from multiple sources, where clients need to request exactly the fields they need.
Minimizes over- and under-fetching and stays language-agnostic with built-in introspection. The tradeoff: a real up-front learning curve and more moving parts (schema, resolvers) than REST or tRPC.
Use it when: Public APIs, multi-language clients, or anywhere long-term backward compatibility and broad tooling support (OpenAPI/Swagger) matter more than type-safety convenience.
Stable, battle-tested, and universally supported by every language and client. The tradeoff: prone to over- and under-fetching, and type safety has to be maintained by hand.
In back-end work, the deciding factor is usually team composition and who consumes the API, not a general ranking. For an internal TypeScript product where the same team owns both ends, tRPC removes an entire class of client-server type bugs for close to no setup cost. The moment an API needs to serve non-TypeScript clients or the public, that advantage disappears and REST’s universality wins. GraphQL earns its complexity specifically when a frontend genuinely needs to shape its own queries against several data sources—it is a poor fit for a simple CRUD API. Treat this as a fit-to-context decision, not a technology allegiance.
Originally published on SD Times, expanded here with production context.
Not universally—tRPC is better for internal TypeScript-only projects where the same team controls client and server, since it gives end-to-end type safety with no code generation. GraphQL is a better fit when a frontend needs to query multiple data sources with flexible field selection, and the API surface is not limited to TypeScript clients.
REST remains the right choice for public APIs, multi-language clients, or products that need long-term backward compatibility and the mature OpenAPI/Swagger tooling ecosystem—cases where portability matters more than the type-safety convenience tRPC or GraphQL offer.
Yes. It's common to use tRPC for internal, TypeScript-only service-to-service or client-to-server calls, and expose a separate REST (or GraphQL) layer for any public or third-party-facing endpoints.