REST vs GraphQL – Key Differences and When to Use Each

Quick answer

REST uses fixed endpoints that return predetermined data shapes; GraphQL uses a single endpoint where the client specifies exactly which fields it needs. Use REST for public APIs, simple services, and when HTTP caching matters. Use GraphQL when multiple clients (web, mobile, TV) need different shapes of the same data, or when round-trips between requests are a measured problem.

The same data request in REST and GraphQL

Both styles use JSON. The difference is who controls the shape of the response.

REST vs GraphQL request shape REST: a client makes four separate GET requests to four endpoints. GraphQL: a client makes one POST request to a single schema endpoint and chooses the fields it needs. REST GraphQL Client needs 4 resources Client defines the shape GET /users GET /posts GET /comments GET /users/42/posts POST /graphql one endpoint one schema client picks the fields Many endpoints server-defined shapes One endpoint client-defined shape Both styles return JSON — the difference is who controls the response shape.

REST: fixed shape, multiple endpoints

# Get a user — returns all fields the server decided to include
GET /api/users/42
→ { "id": 42, "name": "Alice", "email": "...", "address": {...}, "billing": {...}, "preferences": {...} }

# Get that user's posts — separate request
GET /api/users/42/posts
→ [ { "id": 1, "title": "...", "body": "...", "createdAt": "..." }, ... ]

# Two HTTP requests to assemble one screen

GraphQL: one endpoint, client-defined shape

# Single POST to /graphql — client asks for exactly what it needs
POST /graphql
{
  "query": "{ user(id: 42) { name posts { title } } }"
}

→ {
  "data": {
    "user": {
      "name": "Alice",
      "posts": [
        { "title": "My first post" },
        { "title": "Another post" }
      ]
    }
  }
}

# One HTTP request, only the fields requested

The mobile app only needed name and post title. REST returned 30+ fields and required 2 requests. GraphQL returned exactly 3 fields in 1 request.

Quick comparison

FeatureRESTGraphQL
EndpointsMultiple (one per resource)Single /graphql
Response shapeFixed — server decidesFlexible — client decides
Over-fetchingCommonEliminated
Under-fetchingCommon (N+1 requests)Eliminated (one query)
HTTP cachingNative (GET is cacheable)Requires custom approach
VersioningURL versioning (/v1/)Evolve schema without versions
Type systemNone built-in (use OpenAPI)Built-in SDL schema
API documentationOpenAPI / Swagger (external spec)Built-in introspection (GraphiQL / Apollo Studio)
Real-timeSSE / WebSockets (manual)Subscriptions (built-in pattern)
File uploadSimple multipart/form-dataRequires extensions (messy)
Learning curveLow — universally understoodModerate — SDL, resolvers, DataLoader
Best forPublic APIs, simple CRUD, CDN cachingMultiple clients, complex data graphs

Over-fetching and under-fetching explained

Over-fetching

A REST endpoint returns everything it has, regardless of what the client needs. A user profile endpoint might return name, email, address, billing, preferences, avatar URL, last login timestamp, and account tier — but a mobile header component only needs the name and avatar. The other 25 fields are wasted bandwidth on every request.

On a mobile connection at 2G speeds, or multiplied across 10 million API calls per day, over-fetching becomes a real cost — in bandwidth, battery, and parse time.

Under-fetching (the N+1 problem)

A single REST endpoint often does not return all the data a screen needs, so the client makes multiple sequential requests — first for a list, then for details of each item in the list:

# Client building a blog post list page with author names
GET /posts          → [ { id: 1, authorId: 5 }, { id: 2, authorId: 3 }, ... ]
GET /users/5        → { name: "Alice" }
GET /users/3        → { name: "Bob" }
# ... 1 + N requests for N authors

# GraphQL collapses this to one round-trip:
{ posts { title author { name } } }

HTTP caching: REST’s biggest advantage

REST GET requests are natively cacheable by browsers, CDNs (Cloudflare, Fastly), and API gateways. A response to GET /api/products/123 can be cached for 5 minutes at the CDN edge, serving thousands of requests without hitting your origin server.

GraphQL typically uses POST for queries (because query strings can be long). POST requests are not cached by default at the CDN layer. Solutions exist — persisted queries, GET-based queries, CDN-specific rules — but they add operational complexity that REST avoids entirely.

For read-heavy public APIs (product catalogs, documentation, publicly cached data), REST’s caching story is significantly simpler.

Type system and schema

GraphQL has a built-in Schema Definition Language (SDL). Every field, type, and relationship in your API is declared in the schema and automatically introspectable. Clients can query __schema to discover everything the API offers. Tools like GraphiQL and Apollo Studio generate interactive documentation automatically from the schema.

# GraphQL SDL — the schema is the contract
type Query {
  user(id: ID!): User
  posts(authorId: ID): [Post!]!
}

type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  body: String!
  author: User!
  createdAt: String!
}

REST has no built-in equivalent, but OpenAPI (Swagger) fills the same role — documenting endpoints, request shapes, and response types. OpenAPI is a separate document you maintain alongside the code; GraphQL’s schema is the code.

Versioning

REST typically versions via the URL (/v1/users, /v2/users) or a header (API-Version: 2). Old versions must be maintained while clients migrate, creating long-term maintenance debt.

GraphQL avoids explicit versioning by adding new fields and deprecating old ones within the same schema. Clients that query only the old fields continue working; new clients use the new fields. The trade-off is that the schema accumulates deprecated fields over time if you are not disciplined about removing them.

Real-time: Subscriptions vs SSE

GraphQL has Subscriptions as a first-class concept — they are defined in the schema alongside queries and mutations. The underlying transport is typically WebSockets. Clients subscribe to an event (e.g., a new message in a chat room) and receive updates pushed by the server.

# GraphQL Subscription
subscription {
  messageAdded(roomId: "general") {
    id
    text
    author { name }
  }
}

REST does not define a real-time pattern. The common options are Server-Sent Events (SSE) for one-way push, or WebSockets for bidirectional. Both work well but require more manual setup than GraphQL’s built-in subscription pattern.

When to use REST

When to use GraphQL

GraphQL disadvantages

GraphQL’s flexibility comes with trade-offs you have to actively manage. None of these are dealbreakers, but they are the reasons many teams stay on REST for simpler services:

Query cost and security

The same flexibility that eliminates over-fetching also lets a client send a deeply nested, cyclic query that can exhaust your server — a denial-of-service vector that a fixed REST endpoint simply does not have:

# A single malicious query — cheap to send, expensive to resolve
{
  users {
    posts {
      comments {
        author {
          posts {
            comments {
              author { name }
            }
          }
        }
      }
    }
  }
}

Because userspostscommentsauthor can cycle indefinitely, one short request can fan out into millions of database reads. Production GraphQL APIs defend against this with several layers:

REST sidesteps all of this: each endpoint returns a known, bounded shape, so there is no client-controlled query to attack with. This is one of the clearest cases where REST’s rigidity is a security advantage.

Using both: the BFF pattern

Many mature engineering teams run REST and GraphQL side by side. The Backend For Frontend (BFF) pattern uses GraphQL as an aggregation layer in front of multiple REST or gRPC microservices. Each client (web, mobile) has its own BFF that queries the microservices and shapes the response for that client specifically.

# Architecture: GraphQL BFF in front of REST microservices
[Mobile App]  ──── GraphQL ────> [Mobile BFF]  ──>  /users  (REST)
[Web App]     ──── GraphQL ────> [Web BFF]     ──>  /posts  (REST)
                                               ──>  /search (REST)
                                               ──>  /media  (REST)

The microservices stay simple REST services. The GraphQL BFF handles data aggregation and client-specific shaping. This gives you the simplicity of REST internally and the flexibility of GraphQL externally.

Frequently Asked Questions

What is the main difference between REST and GraphQL?

REST exposes fixed endpoints — each URL returns a predetermined shape of data. GraphQL exposes a single endpoint where the client sends a query describing exactly which fields it wants. REST can over-fetch (returning more data than needed) or under-fetch (requiring multiple requests); GraphQL solves both by letting the client specify the exact shape of the response in every request.

Is GraphQL faster than REST?

GraphQL can reduce round-trips by combining multiple data needs into one request, which is faster on slow connections. However, a simple REST endpoint serving one well-known shape is often faster because it can be cached at the CDN or HTTP layer without additional configuration. GraphQL POST requests are not cacheable by default. Performance depends more on implementation quality than on which style you choose.

Should I use REST or GraphQL for a public API?

REST is generally the better choice for public APIs. It has a lower learning curve for consumers, is universally understood with no SDK required, and works well with HTTP caching and standard API gateways. GraphQL is powerful for internal APIs and products with multiple clients (web, mobile, TV) that need different data shapes from the same backend.

Does GraphQL replace REST?

No. GraphQL solves specific problems — over-fetching, under-fetching, rapid frontend iteration — but REST is simpler to cache, monitor, and scale for most use cases. Many companies run both side by side: REST for external and public-facing endpoints, GraphQL as a BFF layer for internal data aggregation across multiple clients.

What does over-fetching mean in REST?

Over-fetching means a REST endpoint returns more fields than the client needs. A mobile app fetching a user profile may only need the name and avatar, but the endpoint returns 30 fields including address, billing info, and preferences. That unused data wastes bandwidth on every request. GraphQL solves this by letting the client request only the specific fields it needs.

What is the N+1 problem in GraphQL?

The N+1 problem occurs when a GraphQL resolver fetches a list of N items and then makes one additional database query per item to resolve a related field. Fetching 100 posts and then the author for each results in 101 database queries. The standard solution is the DataLoader pattern, which batches those N queries into a single query and caches within the same request.

What are the main disadvantages of GraphQL?

GraphQL’s main trade-offs are: no native HTTP/CDN caching (POST requests are not cacheable by default), the N+1 resolver problem which needs DataLoader batching, harder observability because every operation is a single POST /graphql 200 rather than distinct endpoints, field-level authorization that is more complex than per-route checks, and exposure to expensive nested queries that require depth limiting and complexity analysis to prevent abuse. REST avoids most of these by design.

How do you secure a GraphQL API?

Secure a GraphQL API with several layers: depth limiting to reject deeply nested queries, query complexity / cost analysis to score and cap expensive queries before execution, persisted queries (an allow-list of approved queries) so clients cannot send arbitrary ones, server-side timeouts, and mandatory pagination limits on list fields. These defend against denial-of-service from a single malicious query, which a fixed REST endpoint is not vulnerable to.

Working with REST or GraphQL responses?

Format and validate any JSON response in your browser — nothing is sent to a server.

JSON Formatter JSON Validator
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds privacy-first developer tools at JSON Dev Tools.