GraphQL vs REST API: When to Use Which
I rebuilt a scraper last year that needed to fetch job listings from a company using GraphQL. After years of REST, it felt like learning to drive on the other side of the road — everything worked, but my instincts were wrong. I kept reaching for URL parameters that didn't exist and expecting status codes that never came. It took me a full day to do something that would have taken an hour with REST. But the next time I used GraphQL, it took 20 minutes — and I got exactly the data I needed, nothing more.
The GraphQL vs REST debate has raged since Facebook open-sourced GraphQL in 2015. A decade later, the debate is largely settled — not with a winner, but with clarity about when each approach shines. This guide breaks down the technical tradeoffs with real-world scenarios, performance data, and honest recommendations.
The Fundamentals: A Quick Comparison
| Aspect | REST | GraphQL |
|---|---|---|
| Paradigm | Resource-based (nouns as URLs) | Query-based (ask for what you need) |
| Data fetching | Fixed response per endpoint | Client specifies exact fields |
| Endpoints | Multiple (one per resource) | Single (one endpoint, many queries) |
| Over-fetching | Common (returns all fields) | Eliminated (only requested fields) |
| Under-fetching | Common (requires multiple calls) | Eliminated (nested queries) |
| Versioning | /api/v1/, /api/v2/ | No versioning needed (schema evolution) |
| Caching | Simple (HTTP caching, CDNs) | Complex (requires client-side cache) |
| Error handling | HTTP status codes (404, 500, etc.) | Always 200, errors in response body |
| File uploads | Native (multipart form data) | Not native (requires workarounds) |
| Real-time | WebSockets or SSE (separate from REST) | Subscriptions (built-in concept) |
| Tooling maturity | Extremely mature | Good and growing |
The Over-Fetching and Under-Fetching Problem
This is GraphQL's strongest argument. Let's illustrate with a concrete example.
Scenario: Display a job listing card
You need: job title, company name, location, and posting date. Nothing else.
REST approach
GET /api/jobs/123
Response (500+ bytes):
{
"id": 123,
"title": "Frontend Developer",
"company": "TechCorp",
"location": "Baku",
"posted_date": "2026-03-15",
"description": "... 2000 characters ...",
"requirements": ["React", "TypeScript", ...],
"salary_min": 1500,
"salary_max": 2500,
"benefits": [...],
"apply_link": "https://...",
"views": 342,
"applications": 28,
// ... 15 more fields you don't need
}
You wanted 4 fields. You got 20+. That's over-fetching.
Now you also need the company's logo and rating:
GET /api/jobs/123 ← first request GET /api/companies/456 ← second request (to get logo, rating)
Two requests for one card. That's under-fetching.
GraphQL approach
query {
job(id: 123) {
title
location
postedDate
company {
name
logo
rating
}
}
}
One request. Exactly the fields you need. No more, no less.
Performance: The Real Numbers
Performance comparisons between REST and GraphQL are nuanced. Here's what the data shows:
| Metric | REST | GraphQL | Winner |
|---|---|---|---|
| Simple single-resource fetch | ~15ms | ~18ms (query parsing overhead) | REST (marginal) |
| Complex nested data (3+ resources) | ~45ms (3 sequential requests) | ~25ms (single query) | GraphQL |
| Payload size (complex query) | ~12KB (over-fetched) | ~3KB (exact fields) | GraphQL |
| CDN cacheability | Excellent (GET + URL-based) | Poor (POST + body-based) | REST |
| Browser cache utilization | Native (URL-based) | Requires Apollo/urql cache | REST |
| Mobile network performance | Worse (multiple roundtrips) | Better (single request) | GraphQL |
| Server CPU usage | Lower (simpler parsing) | Higher (query planning + validation) | REST |
Key insight: GraphQL wins on network efficiency (fewer requests, smaller payloads) but loses on server-side compute and caching simplicity. For mobile apps and complex UIs, GraphQL's network advantages usually dominate. For simple, cacheable APIs, REST is more efficient.
When to Choose REST
REST is the better choice when:
- You're building a simple CRUD API. If your API is mostly "get this resource, create that resource, update this field," REST maps perfectly. Adding GraphQL complexity is unnecessary.
- Caching is critical. CDN caching, browser caching, reverse proxy caching — all of these work beautifully with REST's URL-based, GET-request model. GraphQL's POST-based queries make this much harder.
- You're building a public API. REST is universally understood. Every developer knows how to call a REST endpoint. GraphQL requires learning a query language and understanding schemas. For public APIs, the lower barrier to adoption matters.
- File uploads are a core feature. REST handles multipart file uploads natively. GraphQL requires workarounds (base64 encoding, separate REST endpoints for uploads, or custom specifications like the GraphQL multipart request spec).
- Your team is small and pragmatic. REST requires less infrastructure. No schema management, no query depth limiting, no resolver optimization. For a team of 2-3 developers, REST's simplicity is a feature.
- Microservices with well-defined boundaries. When each service owns a clear domain with a small API surface, REST's simplicity per service outweighs GraphQL's aggregation benefits.
When to Choose GraphQL
GraphQL is the better choice when:
- You have multiple frontend clients. If a web app, mobile app, and third-party integration all consume the same backend, GraphQL lets each client request exactly what it needs without building separate endpoints for each.
- Your UI requires deeply nested data. Dashboards, social feeds, e-commerce product pages — anything where one view requires data from 5+ related resources. GraphQL's nested queries eliminate the "N+1 request" problem.
- You need rapid frontend iteration. With GraphQL, frontend developers can add or remove fields without waiting for backend changes. This decouples frontend and backend development cycles.
- You're building a data-heavy application. Analytics dashboards, reporting tools, admin panels — applications where different views need different slices of the same data.
- Your API evolves frequently. GraphQL's schema evolution (deprecating fields instead of versioning endpoints) is genuinely better than managing /v1/, /v2/, /v3/ REST APIs.
- Mobile performance matters. On cellular connections, fewer roundtrips and smaller payloads make a measurable difference. GraphQL's single-request model is ideal for mobile.
The Hybrid Approach: Why Not Both?
In practice, many production systems use both REST and GraphQL — and this isn't a compromise, it's a strategy.
Common hybrid patterns
| Component | Protocol | Rationale |
|---|---|---|
| Public API | REST | Universal accessibility, documentation |
| Internal frontend API | GraphQL | Flexible data fetching for UI |
| File upload service | REST | Native multipart support |
| Authentication | REST | Standard OAuth/OIDC flows |
| Admin dashboard | GraphQL | Complex, nested data views |
| Webhooks / events | REST | Simple HTTP callbacks |
| Mobile app | GraphQL | Network efficiency |
| Microservice-to-microservice | gRPC (not REST or GraphQL) | Performance, type safety |
The idea that you must pick one and use it everywhere is a false dichotomy. Use the right tool for each interface.
Common Mistakes with Each Approach
REST mistakes
- Inconsistent naming: Mixing /getUsers, /user-list, /api/v2/users. Pick a convention and stick to it.
- Ignoring HATEOAS: REST's original vision included hypermedia controls. Almost no one implements this, and that's fine — but be aware you're using "REST-ish," not pure REST.
- Over-versioning: Creating /v3/ because you added one field. Use additive changes instead.
- Fat controllers: Endpoints that do too much. Each endpoint should represent one resource operation.
GraphQL mistakes
- No query depth limiting: Without limits, a malicious client can craft a nested query that crashes your server. Always set max depth.
- N+1 query problem on the backend: Resolvers that make individual DB queries per field. Use DataLoader or equivalent batching.
- Exposing your database schema: Your GraphQL schema should represent your API contract, not your database tables.
- Over-engineering for simple needs: Using GraphQL for a three-endpoint CRUD API is like using a chainsaw to cut bread.
My Honest Take
After building and consuming both REST and GraphQL APIs extensively, here's my unvarnished opinion:
- REST is underrated. The industry went through a phase of GraphQL hype where REST was treated as legacy technology. It's not. REST is simple, well-understood, cacheable, and sufficient for 80% of use cases. Don't switch to GraphQL because it's trendy.
- GraphQL is overused in small projects. If you have one frontend, one backend, and a handful of endpoints, GraphQL adds complexity without proportional benefit. The schema management, resolver patterns, and client-side caching setup are non-trivial.
- GraphQL genuinely shines at scale. When you have multiple clients, complex data requirements, and a large team — GraphQL's advantages become overwhelming. The ability for frontend teams to iterate independently of backend is worth the complexity cost.
- The real competitor to both is tRPC/gRPC. For TypeScript monorepos, tRPC gives you type-safe APIs without the ceremony of either REST or GraphQL. For service-to-service communication, gRPC outperforms both. The REST-vs-GraphQL debate often ignores these alternatives.
- Learn both. In 2026, a backend developer who can't work with GraphQL has a gap. A frontend developer who doesn't understand REST fundamentals has a bigger gap. They're both tools. Learn when to use each.
Decision Flowchart
Quick decision framework:
- Is your API public? → REST (lower barrier for consumers)
- Do you have multiple frontend clients with different data needs? → GraphQL
- Is caching critical for performance? → REST
- Do you need deeply nested, relational data in single views? → GraphQL
- Are you a team of 1-3 with simple needs? → REST
- Is this a TypeScript monorepo? → Consider tRPC
- Is this service-to-service communication? → Consider gRPC
- Still not sure? → Start with REST. Migrate to GraphQL later if you hit its pain points.
Action Plan: Getting Started
- If you know REST but not GraphQL: Build a simple GraphQL server with Apollo Server or Strawberry (Python). Implement queries, mutations, and subscriptions. Connect a React frontend with Apollo Client.
- If you know GraphQL but not REST well: Study REST API design best practices. Read "RESTful API Design" guidelines from Microsoft or Google. Understand HTTP methods, status codes, content negotiation, and HATEOAS.
- If you know neither: Start with REST. Build a CRUD API with Express/FastAPI/Spring. Then learn GraphQL as your second API paradigm.
- For your next project: Apply the decision flowchart above. Document why you chose the approach you chose — this becomes interview material.
- Advanced: Learn Federation (for GraphQL microservices), Persisted Queries (for GraphQL caching), and API Gateway patterns (for hybrid architectures).
Sources
- GraphQL Foundation — specification and best practices
- PayPal Engineering — "GraphQL at PayPal" case study
- Netflix Tech Blog — API architecture decisions
- Apollo GraphQL — performance benchmarks
- Microsoft REST API Guidelines
- Google API Design Guide
I'm Ismat, and I build BirJob — Azerbaijan's job aggregator scraping 80+ sources daily.
