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
| Dimension | REST | GraphQL |
|---|---|---|
| Caching | HTTP caching (Cache-Control, ETag, CDN) works per URL | Single endpoint; use persisted queries over GET or client caches (Apollo, Relay) |
| Over- and under-fetching | Mitigated with sparse fieldsets or purpose-built endpoints | Client selects fields |
| N+1 risk | Visible per endpoint | Per-field resolvers; batch with DataLoader |
| Contract | OpenAPI | Schema is the contract; typed codegen |
| Versioning | URL or header versions | Additive schema evolution with deprecation |
| Authorization | Per route | Per field and resolver; easy to miss |
| Cost control | Rate limits per endpoint | Query depth, complexity, and persisted-query allowlists |
| Tooling | curl, browsers, every HTTP client | GraphQL 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-KeyonPOST. - 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.