Development
tRPC vs REST vs GraphQL: Which API Approach to Choose
A practical three-way comparison of tRPC, REST, and GraphQL for full-stack TypeScript applications. Covers type safety, developer experience, client flexibility, caching, performance, and the right choice for each project type.
A New Entrant Changes the Comparison
The REST vs GraphQL comparison has existed for years. tRPC, first released in 2021 and widely adopted by 2024, adds a third option that has changed how full-stack TypeScript teams design their APIs. Understanding all three is now necessary for an informed choice.
tRPC (TypeScript Remote Procedure Call) takes a different approach to both REST and GraphQL. Instead of defining a protocol (HTTP verbs and URLs for REST, a schema language for GraphQL), tRPC shares TypeScript types directly between the server and client. You call server functions from the client as if they were local functions, with full type safety and autocompletion, and zero schema definition overhead.
tRPC: End-to-End TypeScript Type Safety
tRPC works by exporting the server router type and importing it into the client. The client uses this type to infer the available procedures, their input types, and their return types. If you change a server procedure signature, TypeScript immediately reports an error on every client call that breaks. No code generation, no schema file, no separate type sync step.
The developer experience is exceptional for TypeScript monorepos. Autocompletion for procedure names and inputs works in your IDE. Refactoring server function signatures propagates errors to all clients instantly. The tradeoff is hard coupling between client and server: tRPC only works when client and server share the same codebase (a monorepo) or when the server exports its router type as an npm package. tRPC is not suitable for public APIs consumed by third parties or by non-TypeScript clients.
// tRPC: call server procedures from client with full type safety
const user = await trpc.user.getById.query({ id: '123' });
// ^? User — inferred from server return type, no schema neededREST: Universal Compatibility and HTTP Semantics
REST remains the best choice for APIs consumed by multiple client types or by external developers. Every programming language and platform can call a REST API with a standard HTTP client. The URL structure communicates resource semantics. HTTP caching at the CDN and browser level works out of the box. REST documentation with OpenAPI is universally understood.
REST type safety for TypeScript requires additional tooling. openapi-typescript generates TypeScript types from an OpenAPI spec. zodios and ts-rest add end-to-end type safety to REST APIs by sharing Zod schemas between client and server. These approaches can approximate tRPC type safety for REST but add tooling complexity. REST is the correct default for backend APIs that serve multiple clients or need to be publicly documented.
GraphQL: Flexible Queries for Complex Data
GraphQL solves a specific problem: clients with different data requirements querying complex, graph-shaped data. When a mobile app needs 3 fields and a dashboard needs 30 fields from the same data, GraphQL lets each client ask for exactly what it needs in a single request. No over-fetching, no under-fetching.
GraphQL type safety comes from the schema. graphql-codegen generates TypeScript types from the GraphQL schema for both server resolvers and client queries. This is more setup than tRPC but less tightly coupled — the GraphQL schema is a language-agnostic contract that any client can use. GraphQL is the right choice when you have multiple diverse clients, complex relational data that clients need to traverse, or a need to share the API contract with external partners.
Caching and Performance
REST caching is the simplest. GET requests are cached by browsers, CDNs, and HTTP proxies using standard Cache-Control headers. No additional infrastructure needed. This is a significant operational advantage for content-heavy APIs.
GraphQL caching is more complex (all queries to a single POST endpoint by default; persisted queries and GET requests enable CDN caching). Client-side caching in Apollo Client is sophisticated and entity-normalised. tRPC uses React Query under the hood on the client (or alternatives) for client-side caching, and standard HTTP GET for queries which enables basic HTTP caching. For APIs where CDN edge caching is critical, REST has the clearest path.
Decision Framework: Which to Choose
Choose tRPC when you are building a full-stack TypeScript application in a monorepo (Next.js, Nuxt, SvelteKit) and all your clients are TypeScript. The zero-overhead type safety and developer experience are compelling for internal APIs. tRPC works especially well with Next.js App Router and the T3 Stack.
Choose REST when you need a public API for third-party developers, when you need CDN caching for high-traffic endpoints, when clients are not TypeScript, or when your team needs the widest possible documentation and hiring pool. Choose GraphQL when you have multiple client types with different data needs, complex relational data that clients traverse in one request, or when you need to share an API contract with external partners in a language-agnostic format.
- tRPC: TypeScript monorepo, internal APIs, fastest developer iteration, no public third-party clients
- REST: public APIs, external developers, CDN caching priority, non-TypeScript clients, simpler mental model
- GraphQL: multiple diverse clients, complex relational data, external partner API contract
FAQ
Can tRPC replace GraphQL for large applications?
For internal TypeScript monorepos: yes, often. tRPC handles complex data requirements via server-side procedure composition and avoids the GraphQL schema, resolver, and DataLoader overhead. For large public APIs with multiple client types (web, mobile, third-party): no. tRPC requires TypeScript and a shared codebase. GraphQL provides a language-agnostic schema that non-TypeScript clients can use. At scale with many diverse clients, GraphQL Federation is still the strongest architecture.
Does tRPC work with Next.js App Router?
Yes. tRPC v11 added React Server Component support. You can call tRPC procedures directly in server components without a fetch wrapper, and use the tRPC React Query hooks in client components. The T3 Stack (Next.js + tRPC + Prisma + NextAuth) is a popular starting point. There are also examples of using tRPC with Server Actions as an alternative for simpler mutation patterns.
What is ts-rest and how does it compare to tRPC?
ts-rest is a REST API framework that shares Zod schemas between client and server for end-to-end type safety, similar to tRPC but staying on REST principles (multiple endpoints, HTTP verbs, REST-style URLs). It generates an OpenAPI spec automatically from the shared contract. ts-rest is the better choice when you want tRPC-level type safety but need a real REST API (for documentation, CDN caching, or non-TypeScript clients). tRPC is simpler for pure TypeScript internal APIs; ts-rest bridges the gap for TypeScript teams who still need REST.
Is GraphQL too complex for small teams?
For simple applications with one or two client types, GraphQL adds significant complexity with little benefit: schema definition, resolver implementation, DataLoader for N+1, Apollo Client setup, and cache management. tRPC or REST are simpler for small projects. GraphQL pays off at scale when the complexity it solves (over-fetching, multiple client requirements, schema-as-documentation) would otherwise require maintaining multiple bespoke REST endpoints or complex query parameter systems.
Related free tools
If you want to turn this topic into action, use one of ShortIQ's free tools for campaign planning, UTM structure, or QR distribution.
Continue Reading
Explore more guides on link shortener SaaS strategy, Bitly alternatives, and white label link management.
Free newsletter
Get new guides in your inbox
We publish practical guides on dev tooling, prompt engineering, marketing workflows, and deployment. No fluff — straight to the point.
No spam. Unsubscribe any time.
Was this article helpful?
Tell us if this guide solved the problem or what was still missing. We use this to improve the blog and only follow up if you explicitly allow it.