GraphQL vs REST: How They Really Differ

REST exposes many URLs, one per resource, and the server decides what each response contains. GraphQL exposes a single endpoint, usually /graphql, where the client sends a query naming exactly the fields it wants. REST gets free HTTP caching by URL; GraphQL saves round trips and trims unused fields at the cost of more server work.

At a glance
TopicRESTGraphQL
EndpointsOne URL per resourceOne endpoint, often /graphql
Who picks the fieldsThe serverThe client, in the query
Related dataOften several requestsNested in one query
HTTP cachingBuilt in for GET by URLNeeds GET queries, persisted queries or a client cache
ErrorsMostly HTTP status codesAn errors array, often next to partial data
SchemaOptional (OpenAPI)Required and typed

Updated

The comparison below fetches the same screen of data both ways. Pick a scenario to see the REST calls a client would make next to the one GraphQL query that replaces them, and what each response leaves out or adds.

The same data, both ways

GET /api/users/42
GET /api/users/42/posts?limit=3
query Profile {
  user(id: "42") {
    name
    avatarUrl
    posts(first: 3) { title }
  }
}

REST needs a second call for the posts, and both responses carry every field the server includes. The GraphQL query returns just the name, avatar and three post titles.

Which one to choose

Decision table
Your situationLeans toward
Public API that third parties call with curlREST
Many screens, each needing different related dataGraphQL
Heavy CDN caching of read trafficREST, or GraphQL with persisted GET queries
Mobile clients on slow networksGraphQL, fewer round trips
Simple CRUD over a few tablesREST
Several backend services behind one front doorGraphQL as a gateway layer

What goes over the wire

A GraphQL request with fetch

Every operation is a POST of JSON with query and variables keys to the one endpoint. The Accept header asks for the newer GraphQL response type, with plain JSON as a fallback.

const res = await fetch("/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Accept: "application/graphql-response+json, application/json",
  },
  body: JSON.stringify({
    query: "query ($id: ID!) { user(id: $id) { name } }",
    variables: { id: "42" },
  }),
});
const { data, errors } = await res.json();
if (errors) console.warn(errors);

A partial GraphQL response

One field failed, so it is null and the errors entry names it with path. The rest of the data still arrived.

{
  "data": {
    "user": { "name": "Ada", "posts": null }
  },
  "errors": [
    {
      "message": "Posts service timed out",
      "locations": [{ "line": 1, "column": 30 }],
      "path": ["user", "posts"]
    }
  ]
}

Over-fetching and under-fetching

A REST endpoint returns a fixed shape. A list screen that shows only names still downloads every field of every user, which is over-fetching. A detail screen that needs a user plus their posts plus each post's comment count may need several calls, which is under-fetching.

GraphQL moves that choice to the client. The query lists the fields, the server returns that shape and nothing else. The trade is that the server must handle any combination of fields a client might ask for.

Caching works differently

With REST, a URL is a cache key. Browsers and CDNs can store a GET response and reuse it, because GET and HEAD are the cacheable methods. Most GraphQL traffic is POST to one URL, which those caches do not store.

GraphQL clients make up for it in the app: they split responses into objects and store them by id, so two queries that touch the same user share one cached record. graphql.org recommends a globally unique id field for exactly this reason.

Errors and status codes

REST leans on the status line: 404 for a missing record, 401 when not signed in, 422 for bad input. Clients can branch on the code alone.

A GraphQL response has a data key, an errors key, or both. A syntax or validation problem gives errors and no data. A failure inside one field gives partial data where that field is null, and an error entry whose path points at it.

How it works

  1. Pick a scenario such as a profile page with recent posts.
  2. Read the REST requests on the left and the GraphQL query on the right.
  3. Compare the request count shown under the panes.
  4. Read the note to see where each style sends more data than the screen needs.

Tips

Frequently asked questions

Is GraphQL faster than REST?

Not by itself. It can cut round trips and payload size for screens that need related data, but each query may cost the server more to resolve. A well designed REST endpoint for one screen can be just as quick.

Why does a GraphQL error come back with status 200?

A field can fail while others succeed, so the server returns the data it has plus an errors array. The GraphQL over HTTP guidance uses 2xx for partial success; servers answering with application/graphql-response+json use 400 when a request fails validation.

Can GraphQL responses be cached by a CDN?

Yes, if queries are sent with GET, which servers may allow for queries only. Long queries can exceed URL limits, so teams use persisted queries that send a short hash instead of the whole document.

Can I use both in one product?

Many teams do. File uploads, webhooks and simple public resources often stay as REST routes while app screens read through a GraphQL layer that may call those same services underneath.

Does GraphQL need a database of its own?

No. A GraphQL server is a layer of resolvers, small functions that fetch each field from wherever the data lives: a database, a REST service or a file. The schema describes shapes, not storage.

Related: GraphQL mutations, GraphQL fragments, HTTP status codes, JavaScript async and fetch, Webhook vs API.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap