I was in a design review last year when a junior architect proposed replacing our entire REST API layer with GraphQL. His reasoning: “GraphQL is more modern and solves all the problems REST has.” I asked him to list the specific problems he was experiencing with REST. He couldn’t name one. He’d read blog posts, watched conference talks, and concluded that GraphQL was just better.
It’s not. And neither is REST. They’re fundamentally different approaches to API design, each with clear strengths and weaknesses. The right choice depends on your specific use case, your team’s expertise, and the nature of your client-server interactions.
I’ve built APIs using both approaches, from large REST APIs serving hundreds of clients, and GraphQL APIs powering complex frontend applications. Let me walk through the actual architectural differences and when each one makes sense.
REST: Resource-Oriented Architecture
REST (Representational State Transfer) models your API as a collection of resources, each identified by a URL. You interact with resources using standard HTTP methods: GET to read, POST to create, PUT/PATCH to update, DELETE to remove.
GET /api/users/123 → Get user 123
GET /api/users/123/orders → Get orders for user 123
POST /api/users → Create a new user
PUT /api/users/123 → Update user 123
DELETE /api/users/123 → Delete user 123
REST leverages HTTP semantics (status codes, caching headers, content negotiation), which means the entire web infrastructure (CDNs, proxies, load balancers) understands and optimizes REST traffic natively.
REST’s Strengths
Simplicity and ubiquity: Every developer, every framework, every tool understands REST. The learning curve is essentially zero. HTTP client libraries exist in every language. Debugging is straightforward because you can test endpoints with curl.
Caching: REST’s resource-oriented model maps naturally to HTTP caching. A GET request for /api/users/123 returns the same resource every time, so CDNs and browser caches can serve it without hitting your server. This is enormously valuable at scale.
Statelessness: Each REST request contains all the information needed to process it. No session state, no connection context. This makes REST APIs horizontally scalable, since any server can handle any request.
Maturity: REST has been the dominant API paradigm for almost twenty years. The patterns, anti-patterns, tooling, and best practices are thoroughly documented. For guidance on designing REST APIs well, see what makes an API developer-friendly.
REST’s Weaknesses
Over-fetching: When you GET /api/users/123, you get the entire user object (name, email, address, preferences, metadata) even if you only need the name. You’re transferring data the client doesn’t need.
Under-fetching (the N+1 problem): To display a user’s profile page with their orders and addresses, you might need three separate requests: GET user, GET orders, GET addresses. Three round trips, three latency penalties.
Endpoint proliferation: As client needs diversify, REST APIs tend to grow specialized endpoints. /api/users/123/summary, /api/users/123/with-orders, /api/users/123/dashboard-view. Each endpoint serves a specific client need, and the API surface area expands relentlessly.
Versioning headaches: When you need to change a response shape, you either version the endpoint (/api/v2/users), add optional fields with backward compatibility, or break clients. None of these options is painless.
GraphQL: Query-Based Architecture
GraphQL, developed by Facebook and open-sourced in 2015, takes a fundamentally different approach. Instead of multiple resource endpoints, there’s a single endpoint that accepts a query describing exactly what data the client needs.
query {
user(id: 123) {
name
email
orders(last: 5) {
id
total
items {
productName
quantity
}
}
}
}
The server responds with exactly the shape the client requested, no more, no less. The client drives the data requirements, not the server.
GraphQL’s Strengths
Precise data fetching: The client specifies exactly which fields it needs. No over-fetching, no under-fetching. A mobile client that only needs the user’s name and avatar requests just those fields. A dashboard that needs everything requests everything. Same API, different queries.
Single round trip: The query above fetches a user, their orders, and the items within those orders in a single request. In REST, that might require three or more separate requests.
Strong typing and introspection: GraphQL has a schema that defines every type, field, and relationship. Clients can introspect the schema to discover what’s available. This enables excellent developer tooling: auto-completion, documentation generation, query validation.
Evolution without versioning: Adding a field to a GraphQL type doesn’t break existing clients because clients explicitly request the fields they need. Deprecating a field is straightforward: mark it deprecated, and clients that don’t use it are unaffected.
Ecosystem: GraphQL’s schema serves as a contract between frontend and backend, enabling tools like Apollo Client, Relay, and code generation that would be much harder to build for REST.

GraphQL’s Weaknesses
Caching is harder: REST caching works because each URL maps to a cacheable resource. In GraphQL, every request goes to the same URL with a different query body. You lose URL-based caching entirely. Client-side caching (Apollo Cache, Relay Store) compensates, but server-side and CDN caching require additional infrastructure (persisted queries, cache keys based on query hash).
Complexity: Setting up a GraphQL server is more work than setting up a REST API. You need a schema definition, resolvers for every field, a query execution engine, and careful attention to performance. The learning curve for the team is steeper.
N+1 query problem on the server: If your resolver for user.orders hits the database for each user in a list, you’ll execute N+1 database queries. DataLoader (batching and caching library) solves this, but you have to implement it deliberately.
Security concerns: GraphQL’s flexibility means clients can craft expensive queries: deeply nested queries, queries that request large collections. Without query complexity analysis, depth limiting, and rate limiting, a single malicious (or naive) query can bring down your server.
File uploads: GraphQL doesn’t handle file uploads natively. You end up bolting on a REST endpoint or using a spec like graphql-multipart-request-spec, which feels like a workaround.
Monitoring and debugging are harder: Every request hits the same endpoint, so you can’t use URL-based monitoring to distinguish between cheap and expensive operations. You need GraphQL-aware tooling.
Architecture Differences That Matter
In a Three-Tier Architecture
In a three-tier architecture, the API layer sits between the frontend and the backend services. REST maps cleanly to this model, where each backend service exposes REST endpoints, and the API layer aggregates them.
GraphQL often introduces a BFF (Backend for Frontend) pattern, where a GraphQL server sits in front of multiple backend services and resolves queries by calling those services. This consolidation can simplify the frontend at the cost of adding complexity to the middleware layer.
Data Flow Patterns
REST is resource-centric: the server defines the resources and their shapes, and clients consume them.
GraphQL is client-centric: the client defines what it needs, and the server fulfills the request.
This inversion of control has profound implications. With REST, the backend team owns the data contract. With GraphQL, the frontend team effectively drives the data requirements. This can improve frontend development velocity but requires strong coordination to prevent the resolver layer from becoming a mess.
Performance Characteristics
Network performance: GraphQL usually wins because fewer round trips and no over-fetching. For mobile clients on poor networks, this is significant.
Server performance: REST usually wins because simple endpoints are easier to optimize, cache, and scale. GraphQL resolvers can generate complex database queries that are harder to optimize.
Total system performance: Depends entirely on the workload. I’ve seen GraphQL APIs that are dramatically faster than the REST API they replaced (because of eliminated round trips) and GraphQL APIs that are significantly slower (because of unoptimized resolvers and missing DataLoader implementation).

My Decision Framework
After implementing both patterns many times over, here’s when I choose each.
Choose REST When:
- Your API serves many diverse clients (third-party developers, mobile apps, partner integrations). REST’s simplicity makes it more accessible.
- Caching is critical for performance. REST’s cache-friendly model is hard to replicate in GraphQL.
- Your operations are mostly CRUD. REST maps directly to create, read, update, delete operations.
- Your team is small or GraphQL-inexperienced. The operational and debugging complexity of GraphQL is real.
- You’re building a public API. REST is the lingua franca of public APIs. The developer experience of a well-designed REST API with good documentation is excellent.
Choose GraphQL When:
- You have a complex frontend with many data requirements. Dashboards, rich interactive applications, mobile apps that need to minimize network calls.
- Multiple client types need different data shapes from the same backend. Mobile needs a subset, web needs everything, internal tools need different aggregations.
- You’re building a BFF (Backend for Frontend) that aggregates multiple backend services.
- Your team has GraphQL expertise and is willing to invest in the tooling (DataLoader, query complexity analysis, monitoring).
- Rapid frontend iteration is a priority. GraphQL decouples frontend data requirements from backend API changes.
Choose Both When:
- You have a public REST API and an internal GraphQL API. REST for external consumers (simplicity, caching), GraphQL for internal frontends (flexibility, efficiency).
- Different use cases have different requirements. I’ve built systems where the transactional API is REST (CRUD operations, caching) and the analytics API is GraphQL (complex queries, flexible data shapes).
The Hype Cycle Warning
GraphQL went through a hype cycle where it was positioned as a REST replacement. It isn’t. It’s an alternative with different trade-offs. I’ve seen teams adopt GraphQL for simple CRUD APIs and add complexity without gaining any benefit. I’ve also seen teams stick with REST for complex frontend data requirements and build increasingly baroque endpoint structures.
The right answer is almost always boring: use the tool that best fits the problem. Sometimes that’s REST. Sometimes that’s GraphQL. Sometimes it’s both. And for high-throughput internal service-to-service communication where schema enforcement and binary efficiency matter, neither REST nor GraphQL is the right answer: that’s where gRPC and Protocol Buffers come in.

Get Cloud Architecture Insights
Practical deep dives on infrastructure, security, and scaling. No spam, no fluff.
By subscribing, you agree to receive emails. Unsubscribe anytime.
