Skip to main content
Home
HexagonalGuru
Web Development

REST vs GraphQL: How to Choose the Right API for Your Project

HexagonalGuru's avatar

HexagonalGuru

REST vs GraphQL: How to Choose the Right API for Your Project

"REST is dead, everything should be GraphQL now." We've been hearing that for a decade, and meanwhile the vast majority of the APIs running the world — Stripe, GitHub, Twilio — are still REST... or they're GraphQL. The debate is framed wrong: they're not rivals, they're tools that solve different problems.

The right question isn't "which one is better?" but "what problem am I looking at?". This guide gives you the decision framework we use at HexagonalGuru when designing an API for a client: no hype, just the real costs of each option.

The Problem: Choosing by Fashion

We've audited projects running GraphQL on top of three CRUD tables (a cannon to kill a fly, N+1 included) and mobile apps hitting twelve REST endpoints to render a single screen. In both cases the team paid a price: unnecessary complexity or poor performance. Choosing by fashion instead of by problem is technical debt from the very first commit.

REST in Essence

REST models the API as resources (/users/42, /orders) manipulated with HTTP verbs. Its strengths have been proven for twenty years:

  • Simplicity and predictability. Any developer understands GET /orders/42 without documentation.
  • Free HTTP caching. CDNs, proxies and browsers cache GET responses with standard headers. It's the cheapest way to scale reads that exists.
  • Mature ecosystem. OpenAPI, client generators, testing tools, per-endpoint monitoring — it's all been invented already.
  • Semantic status codes. A 404 is a 404. A 429 is a 429. Infrastructure (load balancers, WAFs, dashboards) understands them.

GraphQL in Essence

GraphQL exposes a single endpoint and a typed schema; the client asks for exactly the fields it needs in one query:

REST: three requests with overfetching versus GraphQL: a single query with the exact fields

Its real strengths:

  • No more overfetching and underfetching. The client gets what it asks for — not one field more, not one request less. On mobile, over bad networks, you feel it.
  • A schema as a living contract. Introspection, autocompletion and query validation against the schema. Documentation can't drift because it is the schema.
  • Independent frontends. Web, iOS and Android request different shapes of the same data without asking the backend for changes. It shines as a BFF (Backend for Frontend) layer.

What Nobody Tells You About GraphQL

GraphQL isn't free. These are the costs we see over and over in our audits:

  1. The resolver N+1. One innocent nested query can fire hundreds of database queries. Without dataloaders (batching), your GraphQL is a performance-problem generator.
  2. Caching gets complicated. Everything is a POST to the same endpoint: HTTP caching stops working. You get to reinvent it (persisted queries over GET, field-level cache, TTLs in the schema).
  3. Rate limiting by complexity. Counting requests is no longer enough: a single query can cost a thousand times more than another. You need depth and query-cost limits.
  4. Errors with 200 OK. Error responses arrive with HTTP 200 and an errors array inside. Your dashboards and alerts need adapting.
  5. Learning curve and tooling. Schema, resolvers, dataloaders, fragments, client-side code generation... it's another layer of knowledge the team must master.

The Same Example in Both Worlds

A profile screen showing the user's orders. In REST, the client orchestrates:

GET /users/42
GET /users/42/orders?limit=5
GET /products/101

Each response carries all its fields (overfetching) and the client chains requests (underfetching). In GraphQL, a single query declares the exact shape:

query {
  user(id: 42) {
    name
    orders(limit: 5) {
      id
      total
      lines { product { name } quantity }
    }
  }
}

One request, zero spare fields. The trade-off? The backend must resolve that query without turning it into an N+1 festival:

// Resolver with dataloader: one query per batch, not one per order
public function lines(array $orderIds): array
{
    $lines = $this->lines->findByOrders($orderIds); // 1 query
    return array_map(fn ($id) => $lines[$id] ?? [], $orderIds);
}

How to Decide: The Practical Framework

REST vs GraphQL decision tree based on client types, caching needs and domain complexity

Choose REST when:

  • Your API is a reasonably direct CRUD or serves stable, predictable resources.
  • HTTP caching and the CDN are part of your performance strategy (public content, catalogs).
  • Your consumers are third parties or server-to-server integrations: REST with OpenAPI is the common denominator everyone knows how to consume.
  • The team is small and doesn't need another technology to maintain.

Choose GraphQL when:

  • You have several clients (web, mobile, partners) needing very different data shapes over the same domain.
  • Your screens aggregate data from many entities and overfetching is costing you real performance on mobile.
  • It acts as an aggregation layer (BFF) over several internal services: the client sends one query, GraphQL orchestrates.
  • You have a team capable of operating its particularities (dataloaders, persisted queries, complexity limits).

And the answer that most often turns out to be right in large systems: both. REST for integrations and cacheable resources, GraphQL as a BFF for the apps. They coexist just fine.

Common Mistakes

  1. GraphQL as a direct database proxy. Resolvers running one query per field, with no dataloaders. Performance sinks exactly when traffic peaks.
  2. Exposing GraphQL publicly without limits. With no depth or complexity limit, anyone can take your API down with a recursive query.
  3. Sloppy REST. Collections without pagination, responses without field filtering, zero versioning and errors that always return 500. REST wasn't the problem.
  4. GraphQL for a simple CRUD. If your API is four flat entities and a single client, you're paying the cost without collecting the benefit.
  5. Choosing before measuring. If you don't know how many requests your screens make today, you don't know whether you have an overfetching problem or a different one entirely.

The Bottom Line

REST and GraphQL don't compete: they solve different problems. REST wins on simplicity, caching and integrations; GraphQL wins when multiple clients need different data shapes and overfetching genuinely hurts. Decide based on your clients, your domain and your team's capacity — not on the trending article of the week.

Designing an API, or inherited one that doesn't perform as it should? In our audits we measure real behavior (requests per screen, N+1, caching) and hand you a concrete plan. Request a free consultation →

Keep reading: Web performance: a practical guide to speeding up your application.

  • #apis
  • #graphql
Related articles
Shall we start?

Ready to build something that grows with your business?

Tell us your goals and together we will map the design, development and technology route that takes you from vision to measurable results.