Same job, three styles
Snip's dashboard needs, for one link: its long URL, its click count, and clicks by day for the last week. Here's that request in each of the three styles you'll meet most.
REST treats the API as a set of resources with URLs, and uses HTTP's own verbs and status codes on them:
GET /links/x7Kp → 200 { "code": "x7Kp", "long_url": "…", "created_at": "…" }
GET /links/x7Kp/stats → 200 { "clicks": 18420, "by_day": [ … ] }gRPC treats the API as functions you call on a remote server. You define them, and the messages they take, in a schema file; tools generate client and server code in each language. Messages travel as compact binary (Protocol Buffers) over HTTP/2:
service Links {
rpc GetLink (GetLinkRequest) returns (Link);
rpc GetStats (GetStatsRequest) returns (Stats);
}
message GetLinkRequest { string code = 1; }
message Link { string code = 1; string long_url = 2; int64 clicks = 3; }GraphQL exposes one endpoint and a typed schema of everything that can be asked for. The client writes a query naming exactly the fields it wants, across related objects, and gets back just those:
query {
link(code: "x7Kp") {
longUrl
clicks
clicksByDay(last: 7) { day count }
}
}How they compare
| REST | gRPC | GraphQL | |
|---|---|---|---|
| Shape | Resources and HTTP verbs | Functions with typed messages | One endpoint, typed schema, client-written queries |
| Format | Usually JSON | Binary Protocol Buffers | JSON |
| Contract | Often informal; OpenAPI if you add it | Strict schema, generated code | Strict schema |
| HTTP caching | Works naturally for GET | Not really | Hard: most queries are POST to one URL |
| Browsers | Native | Needs a proxy layer | Native |
| Fetching shape | Fixed per endpoint: can be too much or too little | Fixed per method | Exactly what the client asks for |
| Streaming | Not built in | Built in, both directions | Subscriptions, via extra machinery |
| Best at | Public APIs, simple CRUD, cacheable reads | Fast, typed calls between your own services | Many different clients needing different views of related data |
What each one costs
- REST endpoints are designed around resources, not screens. A dashboard may need several calls (link, then stats, then owner) to draw one page, and each response may include fields it doesn't need. That's fine for most APIs and gets painful on slow mobile networks.
- gRPC is fast and strictly typed, which is exactly what you want between your own services. But browsers can't call it directly, binary messages are harder to inspect while debugging, and every client needs generated code.
- GraphQL moves the shape of the query to the client, which is great for the client and hard for the server: a single query can ask for a lot of nested data, so you need limits on query depth and cost, careful batching to avoid one database query per item, and different ways to cache.
Snip's choices
- Public API: REST. Developers can try it with
curl, every language has an HTTP client, andGETresponses can be cached by CDNs and browsers. - Between Snip's own services, if it splits into several: gRPC is a common choice, for typed contracts and speed. 03.08 Service-to-service communication covers how services talk.
- GraphQL: only if Snip grew many client apps that each needed very different slices of the data. One dashboard doesn't justify it.
The choice is rarely all-or-nothing. Many companies use REST at the edge, gRPC inside, and GraphQL for one product surface with demanding clients.
Seen in production
GitHub
2016GraphQL API (API v4)
GitHub said its REST API was responsible for over 60% of the requests made to its database tier. Clients sometimes needed two or three calls to build a complete view of one resource, and responses were full of URL hints while still missing data clients needed: "too much data and didn't include data that consumers needed". GitHub launched a GraphQL API so clients could ask for exactly what they needed in one request.
What we foundA large public REST API moved to GraphQL to cut round trips and over-fetching, and the load they caused.
The GitHub GraphQL APIgithub.blog ↗Snip splits its stats into a separate internal service that the main app calls on every dashboard load. No browsers call it directly. Which style fits best?
Recap
Interview questions
When would you choose REST, gRPC or GraphQL for an API?