REST vs. GraphQL vs. gRPC: The 'Restaurant Menu' Mental Model
Why does GraphQL exist if REST works fine? A mastery guide to API protocols, when to use each, and how gRPC changes the game for internal services.
“Should we use GraphQL?”
The engineering team splits in half. One side says it’s amazing. The other says REST is fine. Nobody can explain the difference without defaulting to “it depends.”
This guide settles it.
This is the Mastery Guide to API Protocols. We’ll use the “Restaurant Menu” model to understand REST (Fixed Menu), GraphQL (Custom Order), and gRPC (Walkie-Talkie between chefs).
Part 1: Foundations (The Mental Model)
REST = The Printed Menu
A REST API is like a restaurant with a printed menu. The kitchen decides what dishes exist. You pick from the list.
- Strength: Simple, universal. Every language and every tool (Postman, curl) understands it.
- Weakness: You get exactly what’s on the menu. If you only want the salad from the “Grilled Chicken Salad Combo,” you still pay for the whole combo. (Over-fetching). Or the menu doesn’t have exactly what you want; the waiter brings you three separate dishes. (Under-fetching).
GraphQL = The Custom Order
GraphQL is like a restaurant where you tell the chef exactly what you want.
- “I want the chicken breast, no sauce, extra rice, and the mango salad dressing.”
- The kitchen makes exactly that. One trip. Exactly what you asked for.
- Strength: No over-fetching. No under-fetching. Perfect for mobile apps where bandwidth is limited.
- Weakness: The kitchen is complex. You must manage a schema. Authorization is harder (is this user allowed to ask for
user.creditCard?).
gRPC = The Chef’s Walkie-Talkie
gRPC is not for customers. It’s for kitchen-to-kitchen communication.
- Chef from the Cold Kitchen radios Chef from the Hot Kitchen: “Steak ready?”
- Uses a binary protocol (not human-readable text) for maximum speed.
- Strongly typed. The contract is defined in a
.protofile. - Strength: Blazing fast (3x-10x JSON/REST). Bi-directional streaming possible.
- Weakness: Not for browsers. The
.protocontract requires shared tooling.
Part 2: The Investigation (The Technical Differences)
1. The N+1 Problem (REST’s Achilles Heel)
Imagine a UI that shows a list of Posts with their Author names.
# 1 request for posts
GET /posts → [{ id: 1, author_id: 5 }, { id: 2, author_id: 8 }, ...]
# Then N requests for each author!
GET /users/5 → { name: "Alice" }
GET /users/8 → { name: "Bob" }
... (For 50 posts → 51 total API calls)
GraphQL’s Fix: One query, exactly the data needed.
query {
posts {
title
author {
name # GraphQL resolves this in ONE roundtrip
}
}
}
2. gRPC: The Proto Contract
The .proto file is the DNA of gRPC — a shared language both client and server understand.
// payment.proto
syntax = "proto3";
service PaymentService {
rpc ProcessPayment (PaymentRequest) returns (PaymentResponse);
rpc StreamUpdates (PaymentRequest) returns (stream PaymentEvent); // Streaming!
}
message PaymentRequest {
string order_id = 1;
double amount = 2;
}
message PaymentResponse {
bool success = 1;
string transaction_id = 2;
}
From this one file, protoc generates strongly-typed client and server code for Python, Go, Java, etc.
Part 3: The Diagnosis (When to Use What)
| Use Case | Best Choice | Why |
|---|---|---|
| Public API (third-party devs) | REST | Universal — any language, any tool. |
| Mobile App (bandwidth matters) | GraphQL | Fetch exactly what the screen needs. No more. |
| Internal Microservices | gRPC | Fast, type-safe, streaming support. |
| Real-time data (chat, live feed) | GraphQL Subscriptions or gRPC Streaming | Built-in streaming. |
| Simple CRUD | REST | Don’t over-engineer. |
| BFF (Backend for Frontend) | GraphQL | Aggregates multiple services for one UI. |
Part 4: The Resolution (Code Examples)
1. REST (Python/Django)
# urls.py
path("posts/<int:pk>/", PostDetailView.as_view()),
# views.py
class PostDetailView(APIView):
def get(self, request, pk):
post = Post.objects.select_related("author").get(pk=pk)
return Response(PostSerializer(post).data)
2. GraphQL (Python/Strawberry)
import strawberry
from typing import List
@strawberry.type
class Author:
name: str
@strawberry.type
class Post:
title: str
author: Author
@strawberry.type
class Query:
@strawberry.field
def posts(self) -> List[Post]:
return Post.objects.select_related("author").all()
schema = strawberry.Schema(query=Query)
3. gRPC (Python)
# server.py (Payment Service)
import grpc
import payment_pb2, payment_pb2_grpc
class PaymentServicer(payment_pb2_grpc.PaymentServiceServicer):
def ProcessPayment(self, request, context):
# request.order_id, request.amount are strongly typed!
success = charge(request.amount)
return payment_pb2.PaymentResponse(
success=success,
transaction_id="txn_123"
)
# client.py (Order Service calling Payment Service)
channel = grpc.insecure_channel("payment-service:50051")
stub = payment_pb2_grpc.PaymentServiceStub(channel)
response = stub.ProcessPayment(
payment_pb2.PaymentRequest(order_id="ord_456", amount=99.99)
)
Final Mental Model
REST -> The Printed Menu. Universal, predictable, sometimes wasteful.
GraphQL -> The Custom Order. Precise, powerful, complex to secure.
gRPC -> The Chef's Walkie-Talkie. Fast, typed, internal only.
Over-fetching -> "I asked for a user but got their entire order history too."
Under-fetching -> "I need author names, but /posts only gives me author_id."
N+1 Problem -> "50 posts = 51 API calls." (GraphQL or SQL JOIN solves this)
Start with REST. Move to GraphQL only when mobile clients complain about data size. Move to gRPC when internal service latency becomes a bottleneck.
Related posts
-
Rate Limiting & Circuit Breaker: The 'Traffic Light & Fuse Box' Mental Model
How do you stop one bad client from taking down your entire API? A mastery guide to rate limiting strategies, circuit breakers, and resilience patterns.
-
API Certificates: The Mastery Guide to Debugging & The Chain of Trust
Stop guessing with SSLErrors. A mastery-level guide to the Chain of Trust, openssl debugging, and proving exactly whose fault it is.
-
Authentication vs. Authorization vs. OAuth: The 'ID Card' Mental Model
Stop mixing up 401 and 403. A mastery guide to AuthN (Who you are), AuthZ (What you can do), and the OAuth Valet Key.
-
Caching & Redis: The 'Sticky Note' Mental Model
Why does Redis make everything faster? A mastery guide to cache invalidation (the hardest problem in CS), eviction strategies, and Redis data types.