Skip to content

REST vs GraphQL vs gRPC

Summary

A neutral comparison of the three main request/response API paradigms covered in Web Services & APIs. REST models resources over plain HTTP and is described with OpenAPI 3.2.1. GraphQL exposes one typed schema (September 2025 edition) that clients query for exactly the fields they need. gRPC calls typed remote procedures defined in Protobuf (Edition 2024) over HTTP/2. None of them is universally best: the consumer, the need for HTTP caching or streaming, and team skills decide. Every fact on this page comes from the topic pages, which were checked on 2026-09-25.

At a Glance

Dimension REST GraphQL gRPC
Model Resources and HTTP methods (GET /orders/42) One endpoint, client-shaped queries, mutations, subscriptions Remote procedure calls on a service (/{package}.{Service}/{Method})
Contract OpenAPI 3.2.1 (2026-09-10), optional but common GraphQL SDL, September 2025 edition (required schema) Protobuf .proto, Edition 2024 (required)
Governance OpenAPI Initiative (Linux Foundation); HTTP semantics in IETF RFC 9110 GraphQL Foundation CNCF, incubating since 2017-02-16
Transport HTTP/1.1, HTTP/2, HTTP/3 HTTP for queries and mutations; WebSocket or SSE for subscriptions HTTP/2. HTTP/3 only in grpc-dotnet (gRFC G2)
Payload JSON (typically) JSON Protocol Buffers (binary); JSON possible
Streaming None built in; add SSE for server push Subscriptions (WebSocket or SSE) Unary plus server, client, and bidirectional streaming
Browser native Yes Yes No: needs gRPC-Web (unary and server streaming only) or Connect
HTTP caching Native: Cache-Control, ETag, CDNs Hard: POST /graphql defeats HTTP caches; use persisted queries over GET, client normalized caches Not HTTP-cacheable (Connect can serve idempotent RPCs over cacheable GET)
Error model HTTP status codes plus RFC 9457 Problem Details Partial data plus an errors array with path and extensions.code 17 status codes in the grpc-status trailer; HTTP status is 200 even for RPC errors
Versioning URI (/v2), header, or dated versions; Deprecation (RFC 9745) and Sunset (RFC 8594) headers Evolve one schema; mark fields @deprecated Add fields with new numbers; never reuse numbers; buf breaking in CI
Main tooling Spectral, Prism, openapi-generator, Swagger UI, Redoc, Scalar graphql-codegen, Apollo Server 5, GraphQL Yoga, GraphiQL, GraphQL Inspector protoc, buf (lint, breaking, generate), grpcurl, Connect
Licensing of key parts OpenAPI spec: Apache-2.0 Apollo Router and Federation: Elastic License 2.0. Cosmo Router, Hive Gateway: Apache-2.0 gRPC: Apache-2.0
Typical fit Public and partner APIs, CRUD, any client language Many first-party frontends with varied data needs, multi-team graphs Internal microservices, streaming, polyglot backends with strict contracts

Sources: Reference — Protocol Comparison, Reference — Specification and Standard Versions, Reference — gRPC Wire Protocol and Defaults.

How a Request Travels

The same "fetch a user and their orders" call looks different on the wire. REST uses one request per resource (or a purpose-built endpoint), GraphQL resolves one query through a router or server, and gRPC calls a typed method with a deadline over a multiplexed HTTP/2 connection.

sequenceDiagram
    participant C as Client
    participant R as REST API
    participant G as GraphQL server or router
    participant S as gRPC UserService
    C->>R: GET /users/42 (If-None-Match ETag)
    R-->>C: 200 OK or 304 Not Modified
    C->>R: GET /users/42/orders
    R-->>C: 200 OK, full order representations
    C->>G: POST /graphql query { user(id: 42) { name orders { total } } }
    G->>G: Validate, resolve fields, batch with DataLoader
    G-->>C: { data, errors } with only the requested fields
    C->>S: GetUser(user_id 42), grpc-timeout 5S
    S-->>C: User message, grpc-status 0 in trailers

Where Each One Wins

REST

  • Reach: every HTTP client, language, and proxy can call it. It is the default for public APIs.
  • Caching: responses are cacheable by browsers and CDNs through Cache-Control and conditional requests (ETag, 304 Not Modified). See Explanation — Cacheable.
  • Standards momentum: OpenAPI 3.2 added streaming media types (itemSchema for SSE and JSON Lines) and the query operation. The HTTP QUERY method (RFC 10008, June 2026) gives REST a standard safe, idempotent search request with a body.
  • Weak spots: over- and under-fetching, several round trips for nested data, and no built-in streaming.

GraphQL

  • Client-shaped data: one request returns exactly the fields the client asks for, which suits several frontends with different needs.
  • Typed schema and tooling: introspection, GraphiQL, and graphql-codegen give strong end-to-end types. The client normalized cache (Apollo Client) updates every view of an entity after a mutation.
  • Multi-team graphs: Federation lets teams own subgraphs that compose into one supergraph. Apollo Router is Elastic License 2.0; Cosmo Router and Hive Gateway are Apache-2.0 alternatives. An open Composite Schemas spec is still Stage 0 (2026-09). See Explanation — Federation.
  • Weak spots: HTTP caching and rate limiting are harder, the N+1 resolver problem needs DataLoader, and query-cost attacks (depth, width, alias batching) need depth limits, complexity scoring, and trusted documents. See Explanation — GraphQL Security.

gRPC

  • Strict contracts and codegen: .proto files generate typed stubs in many languages. buf breaking catches field-number reuse and type changes before merge.
  • Streaming and multiplexing: four RPC types (unary, server, client, bidirectional streaming) over HTTP/2 multiplexed streams, with deadlines that propagate through the call chain.
  • Compact encoding: Protobuf is binary and usually several times smaller and faster to parse than JSON (payload- and library-dependent; no single benchmark is cited). See Explanation — Protocol Buffers.
  • Weak spots: not browser-native, not HTTP-cacheable, harder to debug by hand, and L4 load balancers pin all RPCs of a connection to one backend. Use L7 proxies (Envoy, a service mesh), client-side round-robin, or proxyless xDS. See Explanation — Load Balancing.

Performance

No verified benchmark

The size and latency figures in Reference — Benchmarks are illustrative rules of thumb without published test conditions. This page does not repeat them as facts. Qualitatively, gRPC's binary encoding and HTTP/2 multiplexing reduce payload size and per-call overhead, but for most CRUD APIs database and downstream calls cost far more than serialization. Pick a paradigm for its contract, tooling, and client fit first, and measure your own payloads if raw speed matters.

Security Differences

All three share the OWASP API Security Top 10 (2023) risks, such as broken object level authorization (BOLA). Each adds its own concerns:

Concern REST GraphQL gRPC
Main extra risk Browser-facing risks (MIME sniffing, framing, caching of sensitive responses), unvalidated parameters Introspection abuse, depth and width bombs, alias brute force, BOLA through node Plaintext channels, unvalidated message fields, reflection exposed to untrusted callers
Key defenses Strongly typed input validation and size limits, security response headers, object-level authorization checks Disable introspection and field suggestions in production, depth and complexity limits, persisted-query allowlist TLS or mTLS (service mesh or SPIFFE), auth interceptors, protovalidate, message size limits
Details REST Security GraphQL Security gRPC Security

Adjacent Options

  • Connect RPC (CNCF sandbox since 2024-04; @connectrpc/connect 2.2.0): serves gRPC, gRPC-Web, and the Connect protocol on one endpoint over HTTP/1.1 or HTTP/2, with JSON support. Browsers can call it without a translation proxy. See Explanation — gRPC-Web.
  • tRPC (v11, 11.19.0): zero-codegen, end-to-end TypeScript types for a team that owns both client and server. TypeScript-only. See Explanation — tRPC vs Alternatives.
  • SSE, WebSocket, WebTransport, webhooks: for push and real-time traffic rather than request/response. See Reference — Protocol Comparison.

Which One Should I Pick?

Start from the consumer of the API boundary, then narrow by browser reach, data shape, and streaming needs.

flowchart TD
    A["New API boundary"] --> B{"Who calls it?"}
    B -->|"External developers or partners"| REST1["REST + OpenAPI 3.2"]
    B -->|"Your own frontends"| C{"Many clients with<br/>different data shapes?"}
    C -->|"Yes"| D{"Several teams own<br/>parts of the data?"}
    D -->|"Yes"| GQLF["GraphQL with Federation<br/>(Apollo Router, Cosmo, Hive Gateway)"]
    D -->|"No"| GQL["GraphQL server<br/>(Apollo Server, GraphQL Yoga)"]
    C -->|"No, one TypeScript team"| TRPC["tRPC"]
    C -->|"No, polyglot or cache-heavy"| REST2["REST + OpenAPI 3.2"]
    B -->|"Internal services"| E{"Need streaming, strict<br/>contracts, or high call volume?"}
    E -->|"Yes"| F{"Must browsers call<br/>it directly?"}
    F -->|"Yes"| CONNECT["Connect RPC<br/>(gRPC-compatible)"]
    F -->|"No"| GRPC["gRPC"]
    E -->|"No"| REST3["REST + OpenAPI 3.2"]

Combining them

Many platforms use all three: REST at the public edge, a GraphQL router or Backend-for-Frontend for first-party apps, and gRPC between internal services. See Explanation — Protocol Comparison Overview and Explanation — Backend for Frontend.

Sources