Introduction
The GraphQL vs REST debate has produced more heat than light. Having built both in production — a Django GraphQL service and several REST APIs — here's my honest take.
When GraphQL Genuinely Wins
1. Complex, interconnected data with multiple consumers
If your data is graph-shaped (users → posts → comments → reactions → users) and you have multiple clients (web, mobile, third-party) all needing different subsets, GraphQL's query flexibility eliminates the over-fetching/under-fetching problem.
query MobileFeed {
user(id: "123") {
name
avatar
posts(limit: 10) {
title
createdAt
commentCount # Computed field — no separate request
}
}
}
2. Rapid frontend iteration
When frontend engineers can request exactly the shape they need without waiting for backend changes, velocity improves significantly.
3. API consolidation
Replacing 12 REST endpoints with a single GraphQL gateway can simplify mobile network patterns from 6 sequential requests to 1.
When REST Is the Right Call
1. Simple CRUD with well-defined resources
If your API maps cleanly to resources (patients, appointments, prescriptions), REST is simpler to build, cache, and reason about.
GET /patients/:id
POST /appointments
PUT /prescriptions/:id
2. Public APIs
REST with OpenAPI docs is the industry standard for public APIs. GraphQL introspection is powerful but unfamiliar to many consumers.
3. File uploads and streaming
REST handles these naturally. GraphQL multipart uploads are possible but awkward.
4. Aggressive HTTP caching
REST endpoints are cacheable by URL. GraphQL POST requests are not — you need persisted queries or a separate caching layer.
Common Mistakes
| Mistake | Why it hurts | |---|---| | GraphQL for simple internal CRUD | Unnecessary complexity, harder to debug | | REST with heavy nesting (3+ levels) | Should be GraphQL | | No DataLoader in GraphQL | N+1 queries will kill you | | GraphQL without depth limiting | Malicious query can DDoS your database | | REST without versioning strategy | Breaking changes in production |
The DataLoader Problem
This is the #1 reason GraphQL APIs are slow without care:
# ❌ N+1 — one DB query per post
def resolve_author(post, info):
return User.objects.get(id=post.author_id) # Called 100 times for 100 posts
# ✓ DataLoader — batches all author IDs into one query
def resolve_author(post, info):
return info.context.loaders.user.load(post.author_id)
My Decision Framework
Is your data graph-shaped with complex relationships? → GraphQL
Do you have multiple clients needing different shapes? → GraphQL
Is it a public API? → REST
Is it simple CRUD? → REST
Do you need aggressive HTTP caching? → REST
Is the team already fluent in one? → Use that one
The technology matters less than the consistency of your implementation.