Overview

Pick REST for APIs that are mostly resource CRUD, are consumed by many clients, or benefit from CDN caching; pick GraphQL when a client assembles screens from many related entities and round trips or over-fetching dominate; pick tRPC when one TypeScript repo owns both sides. GraphQL’s current specification edition is September 2025. GraphQL on a small CRUD app, or REST for a mobile client that aggregates six entities per screen, are the common mismatches.

Comparison

DimensionRESTGraphQL
CachingHTTP caching (Cache-Control, ETag, CDN) works per URLSingle endpoint; use persisted queries over GET or client caches (Apollo, Relay)
Over- and under-fetchingMitigated with sparse fieldsets or purpose-built endpointsClient selects fields
N+1 riskVisible per endpointPer-field resolvers; batch with DataLoader
ContractOpenAPISchema is the contract; typed codegen
VersioningURL or header versionsAdditive schema evolution with deprecation
AuthorizationPer routePer field and resolver; easy to miss
Cost controlRate limits per endpointQuery depth, complexity, and persisted-query allowlists
Toolingcurl, browsers, every HTTP clientGraphQL clients and IDEs

Pick REST when

Pick REST for the majority of APIs.

  • Public APIs for third-party developers: document with OpenAPI, use cursor pagination, ETags, and Idempotency-Key on POST.
  • Server-to-server calls and webhooks, and anything a CDN should cache.
  • Small teams that want the simplest operational model; see fastapi.
  • Prefer REST or gRPC between internal services; GraphQL is a client-facing layer.
  • Live cursors, presence, and notifications: WebSockets or Server-Sent Events beside REST.

Pick GraphQL when

Pick GraphQL when the client shapes the data.

  • Mobile or multi-surface clients aggregating many entities per view.
  • Several teams own parts of one graph; use federation with a gateway.
  • A typed schema end to end, with client code generated from queries.
  • Not reasons to choose it: REST feeling old, wanting a single endpoint, or frontend preference.
  • Mitigate risks: DataLoader for batching, query depth and complexity limits, persisted queries in production, and field-level authorization tests.

Pick tRPC when

Pick tRPC when client and server live in one TypeScript monorepo and the API is not public. It gives end-to-end types without a schema language or code generation. See monorepo-vs-polyrepo and typescript-vs-javascript.

Migrate

Wrap an existing REST API with thin GraphQL resolvers rather than rewriting it; this is cheap if REST resources are well designed. Moving from GraphQL to REST means designing endpoints around the views your queries served. Do not run both indefinitely; set a deprecation date for one.

FAQ

Should I use REST or GraphQL for a public API?

Use REST for public APIs, documented with OpenAPI and using cursor pagination, ETags, and Idempotency-Key on POST. HTTP and CDN caching work per URL with no extra layer.

When is GraphQL worth the extra complexity?

GraphQL pays off when mobile or multi-surface clients aggregate many entities per view, or several teams own parts of one graph through federation. REST feeling old, wanting a single endpoint, or frontend preference are not reasons to choose it.

How do I cache GraphQL responses?

GraphQL uses a single endpoint, so use persisted queries over GET or client caches such as Apollo or Relay. In production, also apply query depth and complexity limits and persisted-query allowlists.

When should I use tRPC instead of REST or GraphQL?

Use tRPC when client and server live in one TypeScript monorepo and the API is not public. It gives end-to-end types without a schema language or code generation.