Performance Comparison — REST vs GraphQL
In this tutorial, you'll learn about Performance Comparison. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
REST and GraphQL have different performance profiles. REST excels with HTTP caching and simple queries. GraphQL shines with complex data requirements but adds query Parsing and resolver overhead.
What You'll Learn
By the end of this lesson, you will measure and compare REST and GraphQL performance, understand when each is faster, and apply optimization techniques.
Why It Matters
Choosing the wrong API style for your data access patterns can multiply server load by 10x. Understanding performance trade-offs prevents costly architectural mistakes.
Real-World Use
Facebook migrated to GraphQL because REST was causing 10+ round trips per feed load. Their internal benchmarks showed GraphQL reduced data transfer by 70% for complex screens.
Performance Factors
flowchart TD
Query[Client Query] --> Complexity{Query Complexity}
Complexity -->|Simple, Single Resource| REST[REST Faster]
Complexity -->|Complex, Nested| GQL[GraphQL Faster]
REST --> Cache[HTTP Caching]
REST --> Overfetch[Over-fetching]
GQL --> NPlus1[N+1 Problem]
GQL --> Parse[Query Parsing Overhead]
Latency Benchmark
# latency_bench.py
import time
from typing import Any, Callable, Dict, List
class APIBenchmark:
def __init__(self):
self.rest_times: List[float] = []
self.gql_times: List[float] = []
def rest_simulate(self, n_endpoints: int, base_ms: float = 50) -> float:
total = 0
start = time.time()
for _ in range(n_endpoints):
time.sleep(base_ms / 1000)
total += base_ms
elapsed = time.time() - start
self.rest_times.append(elapsed * 1000)
return elapsed * 1000
def gql_simulate(self, n_resolvers: int, resolver_ms: float = 10) -> float:
start = time.time()
time.sleep(5 / 1000) # query parse
for _ in range(n_resolvers):
time.sleep(resolver_ms / 1000)
elapsed = time.time() - start
self.gql_times.append(elapsed * 1000)
return elapsed * 1000
def compare(self, scenario: str, n_calls: int, n_resolvers: int):
rest_time = self.rest_simulate(n_calls)
gql_time = self.gql_simulate(n_resolvers)
print(f"{scenario:30s} REST={rest_time:.0f}ms GQL={gql_time:.0f}ms "
f"({'GQL' if gql_time < rest_time else 'REST'} faster)")
bench = APIBenchmark()
bench.compare("Simple single resource", n_calls=1, n_resolvers=1)
bench.compare("Profile (user + orders)", n_calls=3, n_resolvers=3)
bench.compare("Dashboard (5 resources)", n_calls=5, n_resolvers=8)
bench.compare("Feed (20 items + authors)", n_calls=21, n_resolvers=25)
Expected output:
Simple single resource REST=50ms GQL=15ms (GQL faster)
Profile (user + orders) REST=150ms GQL=35ms (GQL faster)
Dashboard (5 resources) REST=250ms GQL=85ms (GQL faster)
Feed (20 items + authors) REST=1050ms GQL=255ms (GQL faster)
Payload Size Comparison
# payload_bench.py
import json
from typing import Any, Dict, List
class PayloadBenchmark:
def __init__(self):
self.full_user = {
"id": 1, "name": "Alice", "email": "a@x.com",
"phone": "555-0100", "address": "123 St",
"age": 30, "role": "admin", "status": "active",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-06-01T12:00:00Z",
"bio": "Developer", "timezone": "UTC", "avatar": "url",
}
def rest_payload(self, fields_needed: int) -> int:
payload = self.full_user if fields_needed > len(self.full_user) else self.full_user
return len(json.dumps(payload))
def gql_payload(self, fields_needed: int) -> int:
selected = dict(list(self.full_user.items())[:fields_needed])
return len(json.dumps(selected))
def compare(self):
for needed in [2, 5, 10, 15]:
rest = self.rest_payload(needed)
gql = self.gql_payload(needed)
ratio = rest / gql
print(f"{needed:2d} fields needed: REST={rest}B GQL={gql}B ratio={ratio:.1f}x")
bench = PayloadBenchmark()
bench.compare()
Expected output:
2 fields needed: REST=<n>B GQL=<n>B ratio=<n>.x
5 fields needed: REST=<n>B GQL=<n>B ratio=<n>.x
10 fields needed: REST=<n>B GQL=<n>B ratio=<n>.x
15 fields needed: REST=<n>B GQL=<n>B ratio=<n>.x
Server Load Analysis
# server_load.py
from typing import Any, Dict, List
class ServerLoadAnalysis:
def __init__(self):
self.query_count = 0
def rest_request(self, path: str) -> Dict:
self.query_count += 1
return {"path": path}
def gql_resolve(self, fields: List[str]) -> Dict:
self.query_count += len(fields)
return {f: f"<{f}>" for f in fields}
def reset(self):
self.query_count = 0
analysis = ServerLoadAnalysis()
# REST: 1 endpoint per resource
analysis.rest_request("/user")
analysis.rest_request("/user/orders")
analysis.rest_request("/user/friends")
print(f"REST queries: {analysis.query_count}")
analysis.reset()
# GraphQL: resolvers per field
analysis.gql_resolve(["id", "name", "email", "orders", "friends", "settings"])
print(f"GraphQL resolver calls: {analysis.query_count}")
Expected output:
REST queries: 3
GraphQL resolver calls: 6
Common Mistakes
1. Assuming GraphQL is Always Faster
Simple single-resource fetches are often faster with REST because GraphQL adds query parsing overhead.
2. Ignoring N+1 in Performance Benchmarks
Without DataLoader, GraphQL benchmarks hide N+1 queries. Always measure resolver-level database calls.
3. Benchmarking Only on Localhost
Localhost benchmarks ignore network latency where GraphQL's single-call advantage shines.
4. Not Measuring Cached Responses
REST with HTTP caching outperforms GraphQL for repeated reads. Include cache hit ratios in comparison.
5. Overlooking Serialization Cost
Large GraphQL responses can take longer to serialize. Use streaming and pagination for large datasets.
Practice Questions
1. When is REST faster than GraphQL?
For simple single-resource fetches. REST avoids query parsing and can leverage HTTP caching.
2. When is GraphQL faster?
For complex screens needing multiple related resources. One GraphQL call replaces 3-10 REST calls.
3. What is the main server-side cost of GraphQL?
Query parsing, validation, and resolver execution. Each field triggers a resolver function.
4. How does caching affect the performance comparison?
REST can cache at CDN level. GraphQL needs resolver-level caching, which is more complex to configure.
Challenge
Build a performance benchmark that measures REST vs GraphQL for an e-commerce product page showing product details, reviews, related products, and seller info.
FAQ
Mini Project: Performance Tester
# performance_tester.py
import time
from typing import Any, Callable, Dict, List
class PerformanceTester:
def test_rest(self, n_calls: int, handler: Callable) -> float:
start = time.time()
for _ in range(n_calls):
handler()
return (time.time() - start) * 1000
def test_graphql(self, n_queries: int, queries: List[str], handler: Callable) -> float:
start = time.time()
for q in queries * n_queries:
handler(q)
return (time.time() - start) * 1000
tester = PerformanceTester()
rest_time = tester.test_rest(100, lambda: None)
gql_time = tester.test_graphql(100, ["{users{name}}"], lambda q: None)
print(f"REST x100: {rest_time:.2f}ms")
print(f"GraphQL x100: {gql_time:.2f}ms")
Expected output:
REST x100: <n>.ms
GraphQL x100: <n>.ms
What's Next
You understand performance comparison. Next, explore use cases, then hybrid REST + GraphQL approach.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro