Cloud Tech by Victor
BackendIntermediate

REST vs GraphQL

The real trade-offs between REST and GraphQL, over/under-fetching, caching, versioning, and when each one is the better default for an API.

Updated 2026-06-203 min read

Overview

REST and GraphQL are two different answers to "how should a client ask a server for data." REST models an API as a set of resources, each with a URL and a fixed response shape per HTTP verb; GraphQL models an API as a single typed schema that clients query with exactly the fields they need. Neither is universally better, REST's simplicity and native HTTP caching make it the right default for most APIs, while GraphQL's flexible field selection pays off specifically when many different clients need meaningfully different shapes of the same underlying data.

Quick Reference

RESTGraphQL
ShapeFixed per endpointClient-specified per query
Over/under-fetchingCommonSolved by design
CachingNative (HTTP, CDN, browser)Requires client-side normalized cache
VersioningNew endpoint or /v2/ prefixSchema evolves additively, fewer breaking versions
Tooling maturityVery mature, ubiquitousMature, more setup (schema, resolvers)
Best fitSimple, resource-shaped APIsMany client shapes, complex data graphs

Syntax

http
# REST - fixed response shape per resource
GET /api/users/42
GET /api/users/42/orders?status=pending
graphql
# GraphQL - client specifies exactly the fields it needs
query {
  user(id: 42) {
    name
    orders(status: PENDING) {
      id
      total
    }
  }
}

Examples

javascript
// REST client: shape is whatever the endpoint returns, take it or leave it
const res = await fetch('/api/users/42')
const user = await res.json() // full user object, even if only `name` is needed
javascript
// GraphQL client: request only the fields actually used
const res = await fetch('/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: `query { user(id: 42) { name } }`,
  }),
})
const { data } = await res.json()

Visual Diagram

Common Mistakes

  • Adopting GraphQL for a small, simple API where REST would have been simpler and required no new tooling or caching strategy.
  • Not solving the N+1 query problem in GraphQL resolvers, a naive resolver for orders { user { name } } across a list can issue one database query per item unless batched (typically via a DataLoader pattern).
  • Assuming GraphQL is automatically faster because it avoids over-fetching, a poorly designed schema with expensive nested resolvers can be slower than a well-designed REST endpoint.
  • Ignoring REST's native cacheability and rebuilding equivalent caching logic from scratch on top of GraphQL without a real need for its flexibility.

Performance

  • REST responses can be cached at the CDN/browser layer by URL for free; GraphQL generally cannot, unless using persisted queries (a hashed query id used as a stable cache key) or a normalized client-side cache.
  • GraphQL's flexible querying can let a client accidentally request a deeply nested, expensive query, production GraphQL servers typically enforce query depth/complexity limits to prevent this.
  • Batching (DataLoader or equivalent) is close to mandatory for GraphQL resolvers that fan out to a database or another service, to avoid N+1 query patterns.

Best Practices

  • Default to REST for straightforward, resource-shaped APIs with a small number of client types.
  • Reach for GraphQL when multiple, meaningfully different clients need different shapes of the same data graph.
  • If using GraphQL, batch resolver data-fetching (DataLoader pattern) and enforce query complexity limits from day one.
  • If using REST, design consistent, predictable resource URLs and use query parameters for filtering rather than proliferating near-duplicate endpoints.

Interview questions

What problem does GraphQL solve that REST does not, structurally?

Over-fetching and under-fetching. A REST endpoint returns a fixed shape, so a mobile client that only needs a user's name either gets the whole user object (over-fetching) or the API team has to add a new endpoint or query params for every new shape a client needs (under-fetching, solved by proliferating endpoints). GraphQL lets the client specify exactly which fields it wants in the query itself, so one flexible schema serves many different client needs without new endpoints.

Why is caching harder with GraphQL than REST?

REST responses map naturally to HTTP caching because a GET to a specific URL returns a predictable resource, so CDNs and browsers can cache by URL. GraphQL typically uses a single POST endpoint with a query body, which defeats standard HTTP/URL-based caching, most GraphQL setups instead rely on client-side normalized caches (like Apollo Client or Relay) keyed by object id and field, or persisted queries plus a CDN cache keyed on the query hash.

When would you choose REST over GraphQL for a new API?

When the API is simple, resource-shaped, and consumed by a small number of known clients with similar needs, REST's simplicity, mature tooling, and native HTTP caching usually win. GraphQL earns its added complexity (schema design, resolver N+1 management, more complex caching) when there are many different client shapes to serve, several frontends, mobile plus web, or third-party API consumers, where flexible field selection meaningfully reduces the number of purpose-built endpoints.

References

ShareXLinkedInReddit
Was this page helpful?
Suggest an improvement