Skip to main content

Command Palette

Search for a command to run...

REST vs GraphQL in System Design

A System Design Decision Disguised as a Syntax Debate

Updated
6 min read
REST vs GraphQL in System Design
S
An enthusiast learner, exploring technologies.

Every engineering team eventually has The Meeting.

Someone says:
“REST is outdated. It’s too chatty.”

Someone else fires back:
“GraphQL is going to melt our database.”

Someone mentions caching. Someone mentions developer experience. Meanwhile, your production database is quietly thinking:

“I don’t care what you choose. Just don’t make me suffer.”

This isn’t about trends. It’s not about what’s modern. It’s about trade-offs.

And system design, at its core, is choosing which trade-offs you’re willing to own at 3:00 AM.

Let’s go deep.


The One Difference That Changes Everything

Strip away the tooling. Strip away the hype.

Everything reduces to one shift in control:

REST → The server defines the shape of the data.
GraphQL → The client defines the shape of the data.

That single pivot changes:

  • Performance characteristics

  • Caching strategy

  • Failure modes

  • Operational complexity

  • Team ownership boundaries

GraphQL gives power to the client.

And as Uncle Ben said:

“With great power comes great responsibility.”

Now let’s see what that responsibility really looks like.


Scene 1: The Harmless Dashboard

You’re building a SaaS dashboard. It needs:

  • User profile

  • Devices

  • Alerts

  • Usage metrics

  • Billing summary

In REST

Your frontend might call:

GET /users/1
GET /users/1/devices
GET /users/1/alerts
GET /users/1/usage
GET /users/1/billing

Five round trips. Now scale it:

  • 3,000 concurrent users

  • Auto-refresh every 30 seconds

That’s 15,000 requests every 30 seconds. This is API fanout amplification.

On mobile networks? Painful. On high-latency regions? Worse.


In GraphQL

You collapse this into:

query {
  user(id: "1") {
    name
    devices { status }
    alerts { severity }
    usage { bandwidth }
    billing { amount }
  }
}

One round trip. Precise data. No over-fetching.

For frontend-heavy systems — especially mobile — this is a legitimate win.

So far, GraphQL looks superior. But we haven’t looked inside the backend yet.


What Is a Resolver (And Why You Should Care)?

A resolver is just a function that says:

“When someone asks for this field, here’s how to fetch it.”

Example:

const resolvers = {
  Query: {
    user: (_, { id }) => db.getUserById(id),
  },
  User: {
    posts: (user) => db.getPostsByUserId(user.id),
  }
};

GraphQL executes queries as a tree. Each field can trigger work. Each nested field can hit your database. This is where flexibility becomes dangerous.


Scene 2: The 3:00 AM Database Melt

A developer writes:

query {
  users {
    name
    posts {
      title
      comments {
        text
      }
    }
  }
}

Looks elegant. But without batching (e.g., DataLoader), your backend might execute:

  • 1 query for users

  • 100 queries for posts

  • 1,000 queries for comments

Classic N+1 problem — amplified. I’ve seen this cause:

  • 3× database load overnight

  • Connection pool exhaustion

  • P95 latency jumping from 120ms → 2.4s

GraphQL didn’t fail. Resolver discipline did.

REST makes this harder to do accidentally because endpoints are predefined.

GraphQL lets clients compose arbitrary trees. Flexibility increases the blast radius.


Caching: REST’s Structural Advantage

REST aligns beautifully with HTTP.

GET /products/42

That URL becomes a cache key. Browsers understand it. CDNs understand it. Reverse proxies understand it.

GraphQL usually looks like:

POST /graphql

Every request hits the same endpoint. To cache properly, you now need:

  • Persisted queries

  • Query hashing

  • Sophisticated client caching (Apollo/Relay)

  • Custom CDN strategies

Can GraphQL be cached? Yes.

Is it as structurally simple as REST? No.

And at scale, edge caching can reduce backend load by 60–90%.

The fastest query is the one you never execute.


Versioning & Governance

REST

/api/v1/users
/api/v2/users

Clear. Explicit. But creates duplication and migration overhead.

GraphQL

Avoids versioning via:

  • Field deprecation

  • Schema evolution

  • Backward compatibility discipline

But now you need:

  • Schema governance

  • Breaking-change detection

  • Contract validation

  • Possibly federation

GraphQL removes endpoint versioning pain. It replaces it with schema management complexity.


Error Handling

REST:

  • Uses meaningful HTTP status codes

  • 404, 401, 500 matter

GraphQL:

  • Often returns 200 OK

  • Errors live inside the response body

Monitoring and observability need to adapt accordingly.

Small detail. Big operational impact.


Microservices & API Composition

Imagine your backend is split into:

  • User Service

  • Device Service

  • Billing Service

  • Analytics Service

Frontend needs data from all.

With REST:

  • Multiple calls

  • Or a custom aggregation layer

With GraphQL:

  • It naturally becomes the aggregation layer

  • Merges responses

  • Normalizes output

This is where GraphQL often shines. Not because it’s trendy.

Because API composition is hard — and GraphQL models it well.


Multi-Tenant Reality Check

Now add:

  • Thousands of tenants

  • Different dashboards

  • Different usage patterns

GraphQL gives flexibility.

But without:

  • Query depth limits

  • Complexity scoring

  • Per-tenant rate limiting

  • Execution time caps

One tenant can impact others. In multi-tenant systems:

Isolation > elegance.

REST’s rigidity becomes a safety feature. GraphQL demands guardrails.


Developer Experience (Where GraphQL Truly Wins)

This is GraphQL’s strongest argument. Strict typing enables:

  • Auto-generated TypeScript types

  • No documentation drift

  • Clear API contracts

  • Strong IDE support

The schema is not optional documentation. It is executable documentation. For fast-moving frontend teams, this is massive.


Performance Truth Nobody Likes

At small scale:

Both work perfectly.

At medium scale:

Database design matters more than API style.

At large scale:

Query discipline matters more than protocol.

GraphQL does not automatically improve performance.

REST does not automatically create inefficiency.

Bad queries break both.


When REST Is the Right Choice

Choose REST when:

  • You’re building a public API

  • You rely heavily on CDN caching

  • Your data model is mostly CRUD

  • Operational simplicity matters

  • Your team is small

REST is boring. Boring systems survive incidents.


When GraphQL Is the Right Choice

Choose GraphQL when:

  • Frontend evolves rapidly

  • You have deeply relational data

  • You need API composition

  • Network round trips are costly

  • Your team understands resolver performance

GraphQL is powerful. Power demands discipline.


The Mature Answer: Use Both

The most stable production systems don’t pick sides. They combine them.

Common pattern:

  • Microservices expose REST internally

  • A GraphQL layer acts as Backend-for-Frontend

  • Complex UI queries go through GraphQL

  • Auth, uploads, webhooks remain REST

This isn’t indecision. It’s architecture.


Final Thought

Your database does not care about syntax. It does not care what’s trending on GitHub.

It cares about:

  • Query volume

  • Query complexity

  • Index efficiency

  • Memory pressure

  • Concurrency

So don’t ask:

“Which one is better?”

Ask:

“What failure mode can my system survive?”

Because at 3:00 AM, modern doesn’t matter.

Predictability does.