REST vs GraphQL: Which One, and When?

Blog · 17 Jun 2024

REST vs GraphQL: Which One, and When?

When you design an API, one of the first big decisions you face is: REST or GraphQL? Both answer the question of "how will the frontend talk to the backend," but their approaches differ fundamentally. In this article I'll compare the two beyond the textbook definitions — with real examples and clear answers to "when which one."

Let me say it up front: this isn't an "X is dead, Y won" piece. Both have their place, and the right call is decided by the needs of the project.

A quick reminder first

REST is an architectural approach that represents resources via URLs. Each resource has its own endpoint, and you operate on those resources with HTTP methods (GET, POST, PUT, DELETE).

restGET    /users/42
GET    /users/42/posts
POST   /posts
DELETE /posts/17

GraphQL is a query language developed by Facebook. You have a single endpoint (/graphql), and the client states exactly which data it wants inside the query. The server returns only the requested data.

graphqlquery {
  user(id: 42) {
    name
    posts {
      title
    }
  }
}

The real difference: who shapes the data?

The most critical difference between the two, in my view, is this. In REST, the server shapes the data. The person who designs the endpoint decides what it returns. In GraphQL, the client shapes the data. The frontend asks for exactly what it needs.

This difference touches three concrete problems in day-to-day development.

Over-fetching and under-fetching

In REST, for a screen where you only need a user's name, when you call /users/42 you probably get email, sign-up date, profile photo, address... all at once. You pull more than you need — this is called over-fetching.

The reverse exists too. You want to show a user and their last 5 posts. In REST you usually send two requests:

restGET /users/42
GET /users/42/posts?limit=5

You can't get everything you want in a single request — this is called under-fetching, and it leads to the "N+1 request" problem.

GraphQL solves both at the root. In a single query you get exactly the fields you want, together with nested relationships:

graphqlquery {
  user(id: 42) {
    name
    posts(last: 5) {
      title
      createdAt
    }
  }
}

One request, zero excess. Especially on mobile, where every byte and every round-trip matters, this is a big advantage.

Versioning

In REST APIs, as you add and remove fields the versioning headache begins: /v1/users, /v2/users... To avoid breaking old clients, you have to keep old versions alive.

In GraphQL you generally don't version. Adding a new field doesn't break existing queries; you mark old fields with @deprecated and retire them over time. The schema keeps evolving but stays single.

Type safety and documentation

One of my favorite things about GraphQL: the schema itself is a contract. Every field has a known type, and that gives you automatic documentation, automatic type generation (it works great with TypeScript) and interactive exploration tools like GraphiQL "for free." In REST, reaching the same level requires extra layers like OpenAPI/Swagger.

So where does REST win?

After praising GraphQL this much, let's restore the balance, because REST is still the right choice in most situations.

Caching. This is REST's strongest card. REST is in perfect harmony with HTTP's native caching. A GET /users/42 request can be cached everywhere — the browser, a CDN, an intermediate layer. In GraphQL, since everything goes to a single endpoint via POST, this automatic caching doesn't work; you have to manage the cache yourself at the application level (with something like Apollo Client).

Simplicity. Everyone knows REST. When a new developer joins the team they don't have to wonder what GET /products does. GraphQL has a steeper learning curve: resolvers, schema design, things like DataLoader to fight the N+1 problem...

File upload, simple CRUD. If most of the work is standard "create-read-update-delete," GraphQL adds extra complexity. REST is more direct here.

Monitoring and rate limiting. In REST it's easy to collect per-endpoint metrics, keep logs and apply rate limits. In GraphQL, because every query hits a single endpoint and complexity varies, these are more troublesome — a user can send a huge, deeply nested request in one query.

The same job, two approaches side by side

Say you're building a blog homepage: author info + their last 3 posts + the comment count of each post.

With REST (usually more than one request):

javascriptconst user = await fetch('/api/users/42').then(r => r.json());
const posts = await fetch('/api/users/42/posts?limit=3').then(r => r.json());
const comments = await Promise.all(
  posts.map(p => fetch(`/api/posts/${p.id}/comments/count`).then(r => r.json()))
);

Three (or more) round-trips, and the merging work on the frontend is left to you.

With GraphQL (a single request):

javascriptconst { data } = await fetch('/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: `
      query {
        user(id: 42) {
          name
          posts(last: 3) {
            title
            commentCount
          }
        }
      }
    `
  })
}).then(r => r.json());

One request, and the data arrives already in the shape you want. This example is the classic scenario where GraphQL shines: gathering related, nested data in one shot.

Decision table

SituationRecommendation
Simple CRUD, little related dataREST
HTTP caching is criticalREST
Public API (third parties will use it)REST (more familiar)
Complex, nested, related dataGraphQL
Many different clients (web + mobile + IoT) share one APIGraphQL
Frontend keeps asking backend to “add this field too”GraphQL
Small team, speed-first, no extra infrastructureREST

Conclusion

My practical rule is this: when in doubt, start with REST. It's enough for most projects, everyone knows it, the infrastructure is ready. Make the move to GraphQL when you feel real pain — when the frontend team keeps complaining about over-fetching, when you send 5 separate requests for a single screen, or when you need to serve many data shapes to many kinds of clients.

The two aren't enemies either. Many teams use both together: REST for the outward-facing, cacheable endpoints; GraphQL for complex internal screens. The right answer isn't "which is cooler" — it's "what is this project's real pain."

Which one do you use in your project, and why? I'm curious — let me know.

← All posts