API

REST vs GraphQL vs gRPC: How to Pick the Right API Style

Three popular API styles, each with strong opinions about how clients and servers communicate. Learn the architectural fit for each, the real performance numbers, and when hybrids beat purity.

13 min read

Three different philosophies. REST organizes around resources. GraphQL organizes around queries. gRPC organizes around procedures. Each makes different trade-offs for client simplicity, performance, evolution, and tooling. The right choice depends less on style preference than on who your clients are and what they need.

The three architectures

REST (Representational State Transfer)

Resources at URLs, manipulated via HTTP verbs (GET, POST, PUT, DELETE). Stateless, cache-friendly, usually JSON over HTTP. The default for public APIs in 2026.

GraphQL

A single endpoint that accepts queries describing what data the client needs. The server returns exactly that data — no over- or under-fetching. Originally from Facebook, now widely adopted for client-driven applications.

gRPC

RPC-style API using HTTP/2 and Protocol Buffers (protobuf). Strongly typed, code-generated, binary-serialized. Optimized for low latency and high throughput, especially in microservice-to-microservice communication.

Side-by-side comparison

RESTGraphQLgRPC
FormatJSON over HTTPJSON over HTTPProtobuf over HTTP/2
SchemaOpenAPI (optional)SDL (mandatory).proto (mandatory)
CachingHTTP-native, easyApplication-level onlyApplication-level only
Browser supportNativeNativeRequires gRPC-Web proxy
StreamingSSE / WebSocketSubscriptions (WebSocket)Native bidirectional
PerformanceGoodGood (with caching)Excellent
Best forPublic APIs, CRUDFrontend apps with many resourcesMicroservices, mobile

When REST wins

  • Public APIs. Lowest tooling barrier. curl works without a client library. Documentation patterns are well-understood (OpenAPI/Swagger).
  • Simple CRUD applications. Resources at URLs map naturally to entities. Standard HTTP verbs handle most operations.
  • HTTP caching is critical. CDN-friendly, browser cache-friendly. ETag, Cache-Control work out of the box.
  • Polyglot client landscape. Every language has HTTP and JSON. No code generation required to consume.

REST's real weaknesses

  • N+1 query problem. Loading a list with details requires multiple round trips.
  • Over-fetching. Clients receive fields they didn't ask for, wasting bandwidth.
  • Versioning friction. URL versioning (/v1/, /v2/) creates parallel endpoints to maintain.

When GraphQL wins

  • Frontends with diverse data needs. Mobile vs desktop vs partner apps — each can request exactly what it needs.
  • Aggregating multiple backend services. One GraphQL gateway in front of many microservices.
  • Rapid frontend iteration. Adding a new field doesn't require a new endpoint.
  • Strongly typed schema for full-stack TypeScript. Codegen produces type-safe clients.

GraphQL's real weaknesses

  • Complex queries can DoS the server. A deeply nested query can fetch enormous amounts of data. Need query depth/complexity limits.
  • Caching is harder. No HTTP-level caching since everything is POST to one endpoint. Application-level caching needed (Apollo Client, urql).
  • N+1 inside the resolver. Naive implementations fetch related data per item. DataLoader pattern is mandatory.
  • Operational tooling immature compared to REST. Logging, monitoring, rate limiting all need GraphQL-aware tooling.

GraphQL isn't a free lunch

The promise of "client gets exactly what it asks for" sometimes hides massive backend complexity. Schema design, resolver performance, and authorization at the field level are non-trivial. Many organizations adopt GraphQL and discover the operational burden was higher than the REST it replaced.

When gRPC wins

  • Internal microservice communication. Strongly typed, fast, code-generated — perfect for service-to-service.
  • Mobile apps with bandwidth constraints. Protobuf is ~3–10x smaller than equivalent JSON.
  • Real-time bidirectional streaming. HTTP/2 multiplexing and native streaming.
  • Polyglot stacks where types matter. Generate consistent client/server code for Go, Java, Python, JS, etc.

gRPC's real weaknesses

  • Browser support is awkward. Direct gRPC doesn't work from browsers; need gRPC-Web proxy.
  • Tooling barrier. Requires installing protoc, configuring code generation. Higher entry cost than REST.
  • Debugging. Binary protocol — can't curl or eyeball requests.
  • Public API friction. Third-party developers prefer REST. gRPC for public APIs is rare.

The hybrid approach

Most large systems use multiple styles. A common pattern:

  • REST or GraphQL at the edge (browser/mobile/public).
  • gRPC internally between services.
  • Async messaging (Kafka, SNS, Pub/Sub) for events between services.

This trades architectural purity for pragmatic fit-for-purpose. The boundary is the API gateway, which translates between styles.

Performance reality check

Benchmark numbers from various studies:

  • REST/JSON: baseline.
  • GraphQL: roughly 1–3x slower than equivalent REST due to query parsing and resolver overhead. Caching can reverse this for cacheable queries.
  • gRPC: 5–10x faster than REST for high-throughput service-to-service. Less dramatic for low-volume calls.

For most user-facing APIs (under 1,000 RPS per service), the performance difference is irrelevant compared to network latency and database queries. Choose based on developer experience and architectural fit, not raw performance — until you can prove otherwise with profiling.

The decision framework

  1. Public API for third-party developers? REST. Universal tooling.
  2. Internal microservice communication? gRPC if performance matters, REST if it doesn't.
  3. Frontend with many entities and views? GraphQL.
  4. Mobile app with bandwidth constraints? gRPC or GraphQL.
  5. Real-time streaming? gRPC bidirectional streams or WebSockets.
  6. Simple CRUD app, single team? REST. Don't over-engineer.

Common mistakes

  • "GraphQL solves all our problems." Adopting GraphQL without addressing N+1, complexity limits, and authorization patterns leads to slower systems with worse caching.
  • "REST is old, we should use GraphQL/gRPC." Style choice should follow requirements, not novelty.
  • Mixing styles randomly. Within a single boundary, pick one. Hybrid systems work when boundaries are clear.
  • Ignoring code generation for protobuf/GraphQL. The type-safety win is the main reason to choose them. Skipping codegen reduces them to slower JSON.
  • Versioning REST badly. Either version everything (URL prefix, accept header) or commit to never breaking compatibility. Mid-states cause client confusion.

Key Takeaways

  • REST: universal tooling, HTTP-native caching, best for public APIs and simple CRUD.
  • GraphQL: client-driven queries, single endpoint aggregating multiple backends, best for frontends with diverse data needs.
  • gRPC: binary protocol, strongly typed, fast, best for microservices and bandwidth-constrained mobile.
  • Performance differences are real (gRPC 5–10x faster than REST for high-throughput) but often dominated by database and network latency.
  • Most large systems use hybrids: REST/GraphQL at the edge, gRPC internally, async messaging for events.