swelearn 0.1.0

Offline terminal-based SWE interview prep tool
---
title: "gRPC vs REST vs GraphQL"
tags: [grpc, rest, graphql, api-design, protobuf, http2, protocols]
difficulty: medium
estimated_time: 20min
---

## Overview

**REST (Representational State Transfer)** is the dominant API style for public-facing services. It uses HTTP verbs (`GET`, `POST`, `PUT`, `DELETE`) and URLs to represent resources. Responses are typically JSON. REST is stateless, cacheable, and widely understood — every language, framework, and tool supports it. Its weakness is **over-fetching** (getting more fields than needed) and **under-fetching** (needing multiple requests to assemble a view), and the lack of a strict contract makes client/server drift common without tooling.

**gRPC** (Google Remote Procedure Call) uses **Protocol Buffers** (`protobuf`) for schema definition and binary serialization, running over **HTTP/2**. You define services and messages in a `.proto` file; the compiler generates strongly-typed client and server stubs in any supported language. The result: significantly smaller payloads (binary vs JSON), multiplexed streams, bidirectional streaming support, and compile-time API contracts. gRPC is the standard for internal microservice communication at scale. Its weakness: no native browser support (requires grpc-web proxy), harder to debug (binary protocol), and tooling is heavier than REST.

**GraphQL** (Facebook, 2015) lets clients specify exactly which fields they want in a single query, solving REST's over/under-fetching problem. A single `/graphql` endpoint handles all queries and mutations. The schema is strongly typed and self-documenting via introspection. GraphQL is ideal for product APIs consumed by multiple clients (web, mobile, third-party) with varying data needs. Its weaknesses: query complexity can be unbounded (N+1 query problem, deeply nested queries), caching is harder (POST requests are not cached by CDNs by default), and schema evolution requires careful management.

```
REST vs gRPC vs GraphQL Comparison:

Feature           REST              gRPC              GraphQL
-----------       ----              ----              -------
Protocol          HTTP/1.1 or 2     HTTP/2 only       HTTP/1.1 or 2
Payload           JSON (text)       Protobuf (binary) JSON (text)
Schema/Contract   Optional (OAS)    Required (.proto) Required (SDL)
Type Safety       Loose             Strong (compiled) Strong (runtime)
Browser Support   Native            grpc-web proxy    Native
Streaming         No (SSE/WS side)  Bidirectional     Subscriptions
Caching           HTTP cache        Limited           Complex (POST)
Debugging         curl, Postman     grpcurl, Evans    GraphiQL, Apollo
Best For          Public APIs       Internal services Flexible clients
Over-fetching     Yes               Yes               No
Code Gen          Optional          Required          Optional

gRPC Streaming Modes:
  Unary:              Client req -> Server resp  (like REST)
  Server streaming:   Client req -> Server stream (like SSE)
  Client streaming:   Client stream -> Server resp
  Bidirectional:      Client stream <-> Server stream (like WebSocket)

REST Resource Example:
  GET  /users/123          -> {id, name, email, address, orders...}
  GET  /users/123/orders   -> [{id, items, total...}, ...]
  (2 requests; may over-fetch fields you don't need)

GraphQL Equivalent:
  POST /graphql
  { user(id: "123") { name orders { id total } } }
  -> exactly {name, orders: [{id, total}]}
  (1 request; exactly the fields requested)
```

## When to Use

- **REST**: Public APIs, third-party integrations, any API consumed by unknown clients. Default choice when interoperability and simplicity matter. Well-understood HTTP caching applies directly.
- **gRPC**: Internal service-to-service communication, polyglot microservices (one `.proto` defines the contract for all languages), high-throughput services where protobuf's smaller payload size matters, or where bidirectional streaming is needed.
- **GraphQL**: Product APIs serving multiple client types (iOS app, Android app, web app) with different data shapes. BFF (Backend for Frontend) pattern where a GraphQL layer aggregates multiple downstream REST/gRPC services. Also excellent for rapid product iteration where the data graph changes frequently.
- **When to combine**: Use gRPC internally between services for efficiency; expose a REST or GraphQL API externally for consumers. This is standard practice — gRPC for the data plane, REST/GraphQL for the API plane.

## Trade-offs & Gotchas

- **N+1 problem in GraphQL**: Naively resolving a list of users, each with their orders, triggers one DB query per user. Solution: **DataLoader** pattern (batch and deduplicate DB calls within a single request). Critical to implement before exposing GraphQL to production traffic.
- **Query depth and complexity limits**: A malicious or careless GraphQL query can request deeply nested data and exhaust your database. Enforce depth limits (max 10 levels) and complexity scoring (each field costs points, reject queries over a budget).
- **Protobuf schema evolution**: Fields are identified by number, not name, enabling backward compatibility. Never reuse field numbers. Use `reserved` for deleted fields. New fields must be optional to remain forward-compatible with old clients.
- **gRPC error model** is richer than HTTP status codes: 16 canonical status codes (`OK`, `NOT_FOUND`, `UNAVAILABLE`, `DEADLINE_EXCEEDED`, etc.). Map these correctly — don't return `UNKNOWN` for everything.
- **REST versioning**: URL versioning (`/v1/`, `/v2/`) is most visible and cache-friendly. Header versioning (`Accept: application/vnd.api+json;version=2`) is more RESTful but harder to route. Avoid versioning the entire API; prefer additive changes and deprecate fields explicitly.
- **GraphQL and caching**: Since queries are typically `POST`, HTTP caching doesn't apply. Use persisted queries (hash the query, send the hash) to enable GET-based caching. Apollo Client and Relay handle client-side caching via normalized stores.
- **gRPC in the browser**: Browsers cannot speak gRPC directly (HTTP/2 trailers not accessible via Fetch API). Use **gRPC-web** (transcoding proxy, e.g., Envoy) or **Connect** (Buf's protocol that works natively in browsers).

## Key Points for Interviews

- REST = resource-based, JSON, HTTP verbs, widely supported, cacheable, no strict contract by default.
- gRPC = protobuf schema, HTTP/2, binary payload, strongly typed, code generation, bidirectional streaming. Best for internal services.
- GraphQL = client-specified queries, single endpoint, no over/under-fetching, strong schema. Best for product APIs with diverse clients.
- Always mention N+1 + DataLoader when GraphQL is discussed — it's the most common production pitfall.
- gRPC protobuf is ~5-10x smaller and faster to parse than equivalent JSON — significant at high RPC rates.
- Combination pattern: gRPC internal + REST/GraphQL external is standard in FAANG-scale systems.
- gRPC streaming replaces WebSockets for bidirectional communication in service-to-service scenarios.