Background & Context
The landscape of API design has shifted dramatically over the last decade, moving from rigid structures to highly flexible and type-safe alternatives. We began with the ubiquity of REST, an architectural style formalized by Roy Fielding in 2000 that leverages the native capabilities of HTTP. For years, REST has been the backbone of the internet, using standard methods like GET and POST to manipulate resources. However, as frontend applications grew in complexity, the limitations of REST—specifically the issues of over-fetching and under-fetching data—became significant bottlenecks. This friction led to the birth of GraphQL at Facebook in 2015, which introduced a revolutionary query language allowing clients to define the exact shape of the data they require. More recently, the rise of the TypeScript ecosystem has given birth to tRPC (TypeScript Remote Procedure Call). Unlike its predecessors, tRPC focuses on the developer experience within full-stack TypeScript environments, aiming to eliminate the boundary between the server and the client by leveraging the compiler's own type inference system. Understanding these shifts is crucial for any senior developer looking to build scalable, maintainable modern architectures in an era where type safety is no longer optional.
Key Differences
The architectural philosophies of these three technologies represent different eras of web development. REST is built on the foundation of the web, utilizing URIs to represent resources and standard HTTP methods to define actions. It is inherently stateless and relies on the server to define the shape of the response, often documented via OpenAPI 3.1. GraphQL flipped this model by allowing the client to define the response shape via a strictly typed Schema Definition Language (SDL). This eliminates the over-fetching problem but introduces a complex execution engine on the server. tRPC represents a third path: it removes the abstraction of a language-agnostic API in favor of deep integration with the TypeScript compiler. By sharing types directly, tRPC eliminates the need for code generation tools like GraphQL Code Generator.
- REST: Resource-oriented, uses standard HTTP status codes (200, 404, 500), and is completely language-agnostic.
- GraphQL: Single-endpoint (/graphql), utilizes a POST-heavy architecture, and requires a schema that acts as a contract.
- tRPC: Procedure-oriented, requires no schema or code generation, and provides "Move to Definition" functionality across the network boundary.
- Type Safety: REST requires manual typing or tools like Swagger; GraphQL uses its own type system; tRPC uses native TypeScript inference.
Real-World Use Cases
- Public Facing APIs: REST remains the undisputed king for external services. If you are building an API for third-party developers, REST is the expected standard because every programming language has a mature HTTP client.
- High-Complexity Data: GraphQL excels in scenarios like social media feeds or data-heavy dashboards where a single view might require data from ten different microservices or database tables.
- Full-Stack TypeScript: tRPC is the optimal choice for teams working in a monorepo with Next.js, Nuxt, or Vite. It provides the fastest developer velocity by providing instant feedback on type errors without running a build step.
- Mobile and Multi-platform: GraphQL is ideal when you have an iOS app, an Android app, and a Web app all consuming the same backend, as each platform can request different fields to optimize for screen size and bandwidth.
- Microservices Aggregation: GraphQL acts as an excellent orchestration layer, sitting in front of multiple legacy RESTful services to provide a unified data graph for the frontend.
Performance & Ecosystem
Performance is not just about raw execution speed; it is about the entire lifecycle of a request. REST is highly efficient for simple resource fetching and benefits from decades of optimization in proxy servers and CDNs that understand standard HTTP caching headers. GraphQL introduces a "parsing tax" where the server must validate the query against the schema before execution, which can be a bottleneck under high load. tRPC v10 and v11 focus on minimizing this overhead by using a simple JSON-RPC 2.0 protocol, functioning as a lightweight wrapper that often outperforms Apollo Server in raw throughput.
- Caching: REST uses native HTTP caching; GraphQL requires normalized client-side caches like Apollo Client; tRPC leverages TanStack Query.
- Tooling: REST has the largest ecosystem (Postman, Insomnia); GraphQL has powerful IDEs like GraphiQL; tRPC is specialized for the TypeScript ecosystem.
- Overhead: GraphQL has the highest CPU overhead due to query parsing; REST and tRPC are significantly lighter on the server.
- Versioning: REST uses versioning in the URL (v1/v2); GraphQL and tRPC generally favor additive changes to avoid breaking the contract.
Which Should You Choose
The decision ultimately hinges on your team's constraints and the project's scope. You should choose REST if you need a public API, have a polyglot backend (e.g., Go, Python, and Node), or require strict adherence to standard HTTP caching mechanisms. It is the safest bet for long-term stability and interoperability. Choose GraphQL if you are managing a complex graph of interconnected data and have multiple frontend platforms that require different data shapes. It is the best choice for large-scale organizations with separate frontend and backend teams. Choose tRPC if you are building a modern full-stack application entirely in TypeScript, particularly within a monorepo. The productivity gains from having the frontend "know" the backend types automatically are transformative.
- Choose REST for: Public APIs, simplicity, and standard caching.
- Choose GraphQL for: Complex data relationships and multi-client ecosystems.
- Choose tRPC for: Maximum developer velocity in TypeScript-only projects.
- Consider maintenance: REST requires keeping docs in sync; GraphQL requires schema management; tRPC requires a shared TypeScript environment.