Choosing REST, GraphQL, or gRPC
The choice between REST, GraphQL, and gRPC is not about which technology is "better." It is about which technology fits your constraints: who is calling the API, what shape the data takes, and how much performance matters. Most companies need REST. Some add GraphQL for specific use cases. Few need gRPC outside of internal service-to-service communication.
REST: The Default for Public APIs
REST is the default because it is the simplest to understand, the easiest to cache, and the most widely supported. Every language has an HTTP client. Every developer knows what GET /users/123 means. Every CDN and proxy understands HTTP semantics.
A typical REST interaction with GitHub's API:
GET /repos/octocat/Hello-World/pulls?state=open&per_page=5
Accept: application/vnd.github.v3+json
Authorization: Bearer ghp_abc123
[
{
"id": 1,
"number": 1347,
"title": "Amazing new feature",
"state": "open",
"user": {
"login": "octocat",
"id": 1
},
"created_at": "2024-01-15T12:00:00Z",
"updated_at": "2024-01-16T08:30:00Z"
}
]
REST works because HTTP already defines the verbs (GET, POST, PUT, DELETE), the status codes (200, 404, 422), and the caching model (ETags, Cache-Control). You are building on a well-understood foundation.
REST wins when:
- Your audience is external developers
- Your data model maps cleanly to resources (users, orders, products)
- You need HTTP caching (CDNs, browser caches, reverse proxies)
- You want the lowest barrier to entry for consumers
GraphQL: When Clients Need Flexible Queries
GraphQL solves a specific problem: when different clients need different slices of the same data, and building a REST endpoint for each combination is impractical.
A mobile app needs a user's name and avatar. The web dashboard needs the user's name, email, billing history, and team memberships. With REST, you either over-fetch (send everything to the mobile app) or build multiple endpoints. With GraphQL, each client asks for exactly what it needs:
# Mobile app query
query {
user(id: "123") {
name
avatarUrl
}
}
# Web dashboard query
query {
user(id: "123") {
name
email
billingHistory {
amount
date
status
}
teams {
name
role
}
}
}
GitHub offers both REST and GraphQL APIs. Their v4 API is GraphQL because repository data has deeply nested relationships (repos have issues, issues have comments, comments have reactions) and different clients need different depths.
GraphQL wins when:
- Multiple clients (mobile, web, internal tools) need different views of the same data
- The data is deeply nested with many relationships
- Over-fetching is a real performance problem (mobile bandwidth, slow connections)
- You want a strongly typed schema that serves as documentation
gRPC: For Internal Service-to-Service
gRPC uses Protocol Buffers for serialization and HTTP/2 for transport. It is fast, typed, and generates client/server code in multiple languages. It is also invisible to browsers, unfriendly to curl, and requires tooling most external developers do not have.
A gRPC service definition:
syntax = "proto3";
service OrderService {
rpc GetOrder (GetOrderRequest) returns (Order);
rpc ListOrders (ListOrdersRequest) returns (ListOrdersResponse);
rpc CreateOrder (CreateOrderRequest) returns (Order);
rpc StreamOrderUpdates (StreamRequest) returns (stream OrderUpdate);
}
message GetOrderRequest {
string order_id = 1;
}
message Order {
string order_id = 1;
string customer_id = 2;
repeated LineItem items = 3;
int64 total_cents = 4;
OrderStatus status = 5;
}
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_CONFIRMED = 2;
ORDER_STATUS_SHIPPED = 3;
}
From this .proto file, protoc generates client and server code in Go, Java, Python, Rust, and others. The schema is the contract, and the generated code enforces it at compile time.
gRPC wins when:
- Both sides are services you control (internal microservices)
- Latency and bandwidth matter (binary serialization is 3-10x smaller than JSON)
- You need streaming (real-time updates, log tailing, bidirectional communication)
- You want compile-time type safety across service boundaries
The Decision Matrix
Three factors determine the choice:
Audience: Public vs Internal
| Audience | Recommended | Reason |
|---|---|---|
| External developers | REST | Universal tooling, lowest barrier to entry |
| External developers with complex data | REST + GraphQL | REST for simple operations, GraphQL for flexible queries |
| Internal services | gRPC | Type safety, performance, streaming |
| Internal + external | REST externally, gRPC internally | Best of both worlds with a gateway |
Data Shape: Fixed vs Flexible
If every client needs the same fields, REST is fine. If different clients need radically different projections of the same data, GraphQL reduces over-fetching. If the data is simple request-response between services, gRPC is the most efficient wire format.
Performance: Latency & Bandwidth
REST over HTTP/1.1 with JSON is the slowest option. GraphQL is similar to REST in terms of transport but can reduce total bytes by eliminating over-fetching. gRPC over HTTP/2 with Protobuf is the fastest: binary encoding, multiplexed connections, and header compression.
For most applications, the performance difference does not matter. If you are processing millions of inter-service requests per second, it matters a lot.
Real-World Patterns
Stripe uses REST exclusively for their public API. Their data model (customers, charges, subscriptions) maps cleanly to resources, their audience is external developers, and simplicity is a core value.
GitHub started with REST (v3) and added GraphQL (v4) because their data is deeply relational. Fetching a repository with its issues, pull requests, and contributors required many REST calls. GraphQL lets clients get everything in one request.
Google uses gRPC extensively for internal services and publishes .proto files for their public APIs (Cloud APIs, Maps, etc.). They provide a gRPC-REST gateway so external developers can call the same services via REST.
Netflix uses GraphQL as a federation layer. Each backend team owns a subgraph, and the gateway composes them into a single schema. Mobile and web clients query the federated graph for exactly the data they need.
The Hybrid Approach
Most companies that grow beyond a certain size end up with a mix:
Browser/Mobile --> REST or GraphQL --> API Gateway --> gRPC between services
|
External devs --> REST --> API Gateway --> gRPC between services
REST for the public API because that is what external developers expect. GraphQL for the frontend teams that need flexible queries. gRPC between internal services for performance and type safety. An API gateway translates between protocols.
Common Pitfalls
- Choosing GraphQL because it is new — if your API has 10 endpoints that return fixed shapes, GraphQL adds complexity without benefit. REST is simpler and better supported.
- Choosing gRPC for a public API — external developers expect to call your API with curl, Postman, or a basic HTTP client. gRPC requires special tooling and does not work in browsers without gRPC-Web.
- Ignoring caching — REST gets HTTP caching for free. GraphQL and gRPC require custom caching strategies. If your data is read-heavy and cacheable, this matters.
- Mixing protocols without a gateway — running REST, GraphQL, and gRPC without a unifying gateway creates an inconsistent developer experience and duplicates cross-cutting concerns (auth, rate limiting, logging).
- Over-engineering for scale you do not have — gRPC's performance benefits are real but irrelevant if you handle 100 requests per second. Start with REST. Optimize when you have evidence.
- Assuming one protocol fits all use cases — a payment endpoint that must be idempotent and cacheable is perfect for REST. A real-time dashboard that needs server-push updates is perfect for gRPC streaming. Match the protocol to the use case.
Key Takeaways
- REST is the default. Use it unless you have a specific reason not to. It is the most widely understood, the easiest to cache, and the most accessible to external developers.
- GraphQL solves the over-fetching problem for clients that need flexible queries over deeply nested data. It adds complexity, so adopt it only when that complexity is justified.
- gRPC is for internal service-to-service communication where performance, streaming, and type safety matter. It is not suitable as a public API without a REST gateway.
- Most mature systems use a hybrid: REST or GraphQL for external consumers, gRPC between internal services, and an API gateway to bridge the protocols.
- Choose based on your constraints (audience, data shape, performance), not on technology trends.