REST — the default that won the web
REST relies on HTTP verbs (GET, POST, PUT, DELETE) and resource-shaped URLs ('/users/123/posts'). It is supported by every HTTP client, cacheable by every CDN, debuggable with curl, and indexable by browsers. The cost: each new requirement often means a new endpoint or query parameter, and over-fetching / under-fetching is common when clients have diverse needs.
GraphQL — clients ask exactly what they need
GraphQL exposes a single endpoint that accepts queries describing the exact data shape the client wants. This eliminates over-fetching and gives front-end teams strong independence. The trade-offs: caching is harder (no per-URL cacheability), query depth limits and persisted queries are needed to prevent abuse, and the server is now responsible for arbitrary join planning.
gRPC — schemas, codegen, and binary speed
gRPC uses Protocol Buffers for schema definition and HTTP/2 for transport, giving you small binary payloads, streaming, and language-agnostic code generation. It is the fastest and most type-safe option for service-to-service traffic. The trade-offs: not directly callable from browsers (you need gRPC-Web), harder to debug by hand, and tooling is more specialized than REST.
Schema evolution
REST evolves by adding new endpoints or versioned URLs. GraphQL evolves by adding fields (deprecate, don't remove) and is generally backwards-compatible by design. gRPC uses Protocol Buffers' explicit field numbers — adding fields is safe, but never reuse a number. Each model works; the difference is how disciplined your team needs to be.
Browser support
- REST: works everywhere, including HTML forms.
- GraphQL: works in any browser that can POST JSON.
- gRPC: not directly — requires a gRPC-Web proxy that translates from browser-friendly HTTP/1.1 to native gRPC.
Caching and CDN
REST is the clear winner for browser and CDN caching: GET requests have stable URLs and standard HTTP cache headers. GraphQL POST requests defeat HTTP caching by default (workarounds: persisted queries, GET with hash, or response-level caching like Apollo). gRPC traffic typically does not go through public CDNs at all.
Observability and debugging
REST is the easiest to debug: tail an access log and you see meaningful URLs. GraphQL hides operation intent inside the query body, so observability requires query naming conventions and APM tools that understand GraphQL. gRPC traffic is binary and needs specialized tracing (OpenTelemetry, grpcurl).
When to pick which
- Public API consumed by third parties: REST (universal client support).
- Frontend with many entity-shape variants (mobile, desktop, embedded): GraphQL.
- Internal microservice traffic at scale: gRPC.
- Real-time bidirectional streams: gRPC (streaming) or GraphQL subscriptions.
- Quick prototype, single client: REST.