How your frontend talks to your backend is one of the most consequential architectural decisions you'll make. tRPC, GraphQL, and REST each solve API design differently. Here's when to use each — and when to avoid them.

Quick Comparison

tRPCGraphQLREST
Type safetyEnd-to-end (automatic)Generated (codegen)Manual (OpenAPI/Swagger)
Data fetchingRPC-style (functions)Query language (flexible)HTTP endpoints (fixed)
Over-fetchingNone (exact return type)Client controls fieldsCommon problem
Under-fetchingNone (single request)Solved (nested queries)Common (N+1 requests)
CachingTanStack Query (manual)Built-in (normalized cache)HTTP caching (ETags, CDN)
File uploadManualComplex (mutations)Simple (multipart)
Public APINo (internal only)GoodBest (standardized)
Learning curveLowHighLow
EcosystemTypeScript-onlyMulti-languageUniversal

tRPC — Typesafe RPC for TypeScript Monorepos

tRPC gives you end-to-end type safety without code generation. Define a procedure on the server, call it like a typed function on the client. The types flow automatically. If you change the server, the client gets type errors at compile time — no runtime surprises.

Strengths: True end-to-end type safety (no codegen needed). Incredibly fast to develop — just write server functions, call them from client. Tiny bundle footprint. Perfect with Next.js App Router. TanStack Query integration for caching and mutations.

Weaknesses: TypeScript-only (both client AND server must be TS). Not suitable for public APIs. Tightly coupled (monorepo or monolith architecture). No built-in caching layer. Not polyglot-friendly (can't call from Python/Go client). More challenging with microservices.

Best for: TypeScript full-stack apps (especially T3 stack), internal tools and admin panels, solo developers or small teams building a single product, Next.js projects.

GraphQL — Flexible Queries for Complex Data

GraphQL lets clients request exactly the fields they need. For complex, nested data models where different clients need different shapes of data, this is transformative. The schema serves as a contract and auto-generated documentation.

Strengths: Clients control response shape (no over/under-fetching). Strong schema as documentation. GraphQL Federation for microservices. Excellent for mobile (bandwidth-sensitive). Rich ecosystem (Apollo, Relay, GraphQL Codegen). Good for public APIs with complex data.

Weaknesses: Steep learning curve. N+1 problem requires dataloader pattern. Caching is complex (normalized cache needed). File upload is clunky. Query complexity attacks (need depth limiting). Overkill for simple CRUD APIs. Bundle size (Apollo Client is heavy).

Best for: Apps with complex, nested data models, mobile apps that need bandwidth-efficient queries, multi-client products (web + mobile + third-party), microservice architectures using Federation.

REST — The Universal Standard

REST is the lingua franca of the web. Every language, framework, and tool supports it. HTTP caching (CDNs, browser, proxies) works out of the box. For public APIs consumed by third parties, REST remains the safest choice.

Strengths: Universal — any client in any language can consume. HTTP caching is built-in (CDNs, browser, Etags). No special client library needed. File upload is trivial (multipart). Battle-tested with decades of tooling. OpenAPI 3.1 for type generation.

Weaknesses: Over-fetching and under-fetching by default. No built-in type safety (OpenAPI is a bolt-on). Endpoint proliferation as features grow. Versioning is manual. No standard for related data (include?expand? nested endpoints?).

Best for: Public APIs, multi-language microservices, file upload APIs, teams that need maximum tooling compatibility, any API consumed by third parties.

Decision Matrix

ScenarioBest Choice
TypeScript full-stack app, one teamtRPC
Complex, nested data (social, e-commerce)GraphQL
Public API for third-party devsREST + OpenAPI
Mobile + web with different data needsGraphQL
Simple CRUD, file uploads, or CDN cachingREST
Internal tool or admin panel (TS stack)tRPC

Bottom line: tRPC for TypeScript monoliths where development speed matters. GraphQL for complex data models with multiple clients. REST for public APIs and when you need universal compatibility. See our REST API Best Practices guide for implementation details.