Architecture & Communication
REST, GraphQL & gRPC
Comparing the three dominant API design paradigms: resource-based REST, client-defined GraphQL, and high-performance binary gRPC.
In short
Comparing the three dominant API design paradigms: resource-based REST, client-defined GraphQL, and high-performance binary gRPC.
Modern software systems are collections of independent services that must communicate constantly over the network. The choice of communication protocol dictates system performance, bandwidth consumption, and client integration complexity. While REST remains the industry standard for public web interfaces due to its simplicity and native HTTP caching, GraphQL has emerged to solve client over-fetching, and gRPC dominates internal microservice pipelines by utilizing HTTP/2 and binary serialization. This lesson compares these three dominant API paradigms.
1. Learning Objectives
- Differentiate between REST, GraphQL, and gRPC communication paradigms.
- Analyze Over-fetching and Under-fetching in REST and how GraphQL resolves them.
- Explain the performance benefits of Protocol Buffers (Protobuf) and HTTP/2 multiplexing in gRPC.
- Understand the N+1 Query Problem in GraphQL and how to solve it using DataLoader.
- Evaluate the trade-offs of API contract styles: OpenAPI vs. GraphQL Schema vs. Protobuf IDL.
- Implement a protocol simulator showcasing REST over-fetching, GraphQL dynamic query parsing, and gRPC binary serialization in Java, Python, and C++.
2. Prerequisites
To get the most out of this lesson, you should be familiar with:
- Client-Server Architecture: HTTP protocols and serialization.
- API Gateway: Protocol translation at the system edge.
3. Why This Topic Matters
Choosing the wrong API protocol can degrade system performance. If a mobile app with limited bandwidth must query a REST endpoint that returns a massive 50KB JSON payload containing unused fields (over-fetching), the user experience suffers. Conversely, if the app must make 5 sequential REST calls to fetch related resources (under-fetching), network latency increases.
Similarly, using JSON over HTTP/1.1 for high-frequency internal microservice calls wastes CPU resources on text serialization and limits connection throughput. Understanding the trade-offs between REST, GraphQL, and gRPC allows system designers to build highly optimized APIs.
4. Real-world Analogy
Think of ordering food at a Restaurant:
REST (The Fixed Combo Menu): You go to a drive-thru and order "Combo #1" (Burger, Fries, Drink). You cannot swap the fries for onion rings or remove the ice from the drink. If you only want the burger, you still get (and pay for) the fries and drink (Over-fetching). If you want an extra sauce, you must place a separate, second order (Under-fetching/Multi-trip).
GraphQL (The Custom Buffet Tray): You walk up to a buffet with a plate and pick exactly what you want: two slices of pizza, one chicken wing, and no salad. You get exactly what you asked for, on a single plate, in a single trip.
gRPC (The Kitchen Conveyor Belt): The kitchen has an automated, high-speed conveyor belt that sends ingredients packed in optimized containers straight to the chefs. The boxes are small, labeled with tags instead of words, and travel continuously in both directions. It is fast and efficient, but requires specialized equipment to read.
5. Core Concepts
- REST (Representational State Transfer): An architectural style centered around resources identified by URIs, manipulated using standard HTTP verbs (
GET,POST,PUT,DELETE). - GraphQL: A query language and runtime for APIs that allows clients to define the exact shape of the response payload.
- gRPC: A high-performance, open-source Remote Procedure Call (RPC) framework developed by Google that runs over HTTP/2.
- Protocol Buffers (Protobuf): gRPC's binary serialization format used to define schemas and serialize structured data efficiently.
- Over-fetching: A scenario where an API response returns more data fields than the client needs.
- Under-fetching: A scenario where an API response does not return enough data, forcing the client to make subsequent network calls.
- Interface Definition Language (IDL): The schema file (e.g.
.proto) used to define the API contract in gRPC.
6. Visualizations
REST vs. GraphQL Payload Fetching
JSON Text Payload vs. Protobuf Binary Payload
7. How It Works Step-by-Step
gRPC Serialization & Transport Execution Path
-
Service Definition: Developers define the service structure and message schemas in a
.protofile. -
Code Generation: The Protobuf compiler (
protoc) generates client stubs and server interfaces in the target programming language. - Client Invocation: The client application calls a method on the generated stub (looks like a local function call).
- Binary Serialization: The stub serializes the input arguments into a compact binary format using Protobuf tags.
- HTTP/2 Transport: The serialized bytes are sent over a multiplexed HTTP/2 connection.
- Server Execution: The server deserializes the binary payload, calls the target service method, and returns a serialized binary response.
8. Internal Architecture
API frameworks utilize different server-side engine configurations to handle requests:
- REST Controller: Maps HTTP routes directly to handler methods, returning JSON serialized payloads.
- GraphQL Resolver Engine: Parses client query strings into an Abstract Syntax Tree (AST), validates fields against the schema, and executes resolver functions recursively to build the response.
- gRPC Compiler Stubs: Generated code wrapper files that handle binary encoding/decoding and route requests to target handlers over persistent HTTP/2 connections.
9. Request Lifecycle
Let's compare how each protocol handles a request to fetch user profile details and their order history:
-
REST:
1. Client sendsGET /users/1. Server queries database and returns user details.
2. Client parses user JSON and reads order link/users/1/orders.
3. Client sendsGET /users/1/orders. Server returns the list of orders.
*(2 round trips, client handles data aggregation)*. -
GraphQL:
1. Client sends aPOST /graphqlquery requesting{ user(id: 1) { name, orders { amount } } }.
2. The engine parses the query, calls theuserresolver, then calls theordersresolver.
3. The engine aggregates the results into a single JSON object matching the query shape and returns it.
*(1 round trip, engine handles data aggregation)*. -
gRPC:
1. Client callsGetUserInfo(UserRequest)on its stub.
2. The stub converts the request into binary and sends it over HTTP/2.
3. The server processes the request, queries user and order details in parallel, serializes the response to binary, and returns it.
*(1 round trip, binary serialization, low latency)*.
10. Deep Dive
HTTP/1.1 vs. HTTP/2 Transport Layer
- HTTP/1.1 (Standard REST/GraphQL): Text-based, head-of-line blocking (requests must be sent sequentially over a TCP connection), and no header compression. This requires browsers to open multiple parallel TCP connections to a single domain to speed up loading.
- HTTP/2 (gRPC): Binary-based, multiplexed (allows multiple requests and responses to be sent in parallel over a single TCP connection), and uses HPACK compression to reduce header overhead. This significantly reduces latency and connection usage.
GraphQL Resolver Performance & The N+1 Query Problem
GraphQL's flexible query model can lead to performance issues. If a client queries a list of 100 users and their orders, the GraphQL engine might call the user resolver once (returning 100 users), and then call the orders resolver *100 times* (once for each user). This results in 101 database queries (the N+1 Query Problem).
To solve this, we use DataLoader. DataLoader aggregates individual request IDs within a single execution block, batching them into a single query (e.g., SELECT * FROM orders WHERE user_id IN (1, 2, ... 100)), reducing database load.
Protobuf Serialization
JSON is a text format that includes field names in every message (e.g., "name": "Alice"). This makes JSON self-describing, but increases payload size and serialization CPU usage.
Protocol Buffers (Protobuf) solves this by stripping field names from the payload. Instead, fields are identified by compact integer tags defined in the schema file (e.g., field tag 1 represents id). This results in small, fast binary payloads.
Browser gRPC Constraints
Browsers do not expose the low-level socket controls required to handle HTTP/2 frame headers directly. As a result, standard web browsers cannot communicate directly with gRPC servers.
To bypass this constraint, you must use gRPC-Web. A gRPC-Web proxy (like Envoy) sits in front of the gRPC server, translating standard HTTP/1.1 client requests from the browser into gRPC-compatible HTTP/2 frames.
11. Production Examples
- Netflix Edge Federation: Exposes a single GraphQL gateway to clients, allowing them to fetch user details and video catalogs in a single request. The gateway translates queries and routes them to internal microservices using gRPC.
- GitHub Public API: Exposes both a REST API for simple, standard integrations, and a GraphQL API for complex queries, giving developers flexibility.
12. Advantages
- REST: Global compatibility, native HTTP caching, and simple debugging (readable text payloads).
- GraphQL: Prevents over-fetching and under-fetching, and allows clients to request exact data shapes.
- gRPC: High performance (fast binary serialization), strict contract enforcement (Protobuf schemas), and native streaming.
13. Limitations
- REST: Prone to over-fetching, and requires multiple round trips for related resources.
- GraphQL: Increased server-side query parsing overhead, complex caching, and vulnerability to deep recursion attacks.
- gRPC: Limited browser compatibility (requires gRPC-Web), and payloads are not human-readable.
14. Trade-offs
- JSON vs. Binary Serialization: JSON is readable and easy to debug, but slow and large. Binary (Protobuf) is fast and small, but requires decoding tools to debug.
- Client Flexability vs. Server Predictability: GraphQL gives clients complete control over query shapes, but makes server performance unpredictable. REST and gRPC enforce fixed endpoints, ensuring predictable server performance at the cost of client flexibility.
15. Performance Considerations
- Serialization Overhead: Protobuf is up to 6x faster to serialize and deserialize than JSON, reducing CPU usage.
- Payload Size: Binary payloads are typically 50-80% smaller than JSON payloads, saving network bandwidth.
- Connection Multiplexing: HTTP/2 multiplexing allows hundreds of requests to be sent concurrently over a single TCP connection, reducing latency.
16. Failure Scenarios
-
GraphQL Query Depth Attacks: Malicious clients can submit deeply nested recursive queries (e.g., a user querying their friends, who query their friends, etc.) that exhaust server CPU and memory.
Mitigation: Enforce query depth limits and query cost analysis at the gateway. -
gRPC Stream Timeout Handling: Long-running server streams can leak resources if clients disconnect without closing the connection.
Mitigation: Configure connection keep-alive settings and enforce timeouts on streams.
17. Best Practices
- Use gRPC Internally: gRPC is best suited for high-speed, inter-service microservice communications.
- Use REST/GraphQL at the Edge: Expose REST or GraphQL public endpoints to simplify web and mobile client integrations.
- Use Schema Versioning: Enforce backward-compatible changes (e.g. only adding optional fields in Protobuf or GraphQL) to prevent breaking clients.
18. Common Mistakes
- Using GraphQL for Microservices: Using GraphQL for internal service-to-service calls. This adds unnecessary query parsing overhead where gRPC would be faster and simpler.
- Neglecting gRPC Browser Limitations: Building gRPC microservices and expecting web applications to connect to them directly without a gRPC-Web proxy.
19. Implementation (Protocol Simulator)
The code tabs below showcase a complete simulation comparing REST, GraphQL, and gRPC in Java, Python, and C++. It demonstrates REST resource fetching, GraphQL dynamic query parsing, and gRPC Protobuf-like binary encoding.
20. Interview Questions
Easy
Q: What is over-fetching, and which API style resolves it natively?
A: Over-fetching is when an API response returns more fields than the client needs (e.g. returning a user's full biography and email address when only the name is displayed). GraphQL resolves this natively by allowing the client to specify exactly which fields it wants in the request payload.
Medium
Q: How does gRPC achieve significantly higher throughput and lower latency compared to REST over JSON?
A: gRPC achieves this through two main factors:
1. Binary Serialization (Protobuf): Instead of encoding data as text-based JSON, gRPC uses Protocol Buffers. This strips field names and serializes values into a compact, schema-defined binary format that requires minimal CPU cycles to serialize/deserialize.
2. HTTP/2 Transport: Unlike HTTP/1.1 which requires sequential connections or multiple TCP sockets, HTTP/2 supports multiplexing multiple requests/responses over a single TCP connection, uses header compression (HPACK), and supports native server-side streaming.
Hard
Q: Explain the N+1 query problem in GraphQL and walk through the exact database query lifecycle when using the DataLoader pattern to resolve it.
A: The N+1 problem occurs when a parent resolver fetches $N$ records (e.g. 100 users), and for each record, the child resolver runs an individual query to fetch related records (e.g. 100 queries to fetch each user's orders), resulting in $N+1$ total database queries.
DataLoader resolves this through Batching and Caching:
1. When the child resolver runs, instead of immediately executing a SQL query, it registers the requested ID with DataLoader, returning a Promise.
2. The GraphQL execution engine continues executing child resolvers for that level, registering all requested IDs.
3. Once the level execution completes, DataLoader executes a batch callback function with all collected IDs (e.g. SELECT * FROM orders WHERE user_id IN (1, 2, ... 100)).
4. DataLoader resolves all pending Promises with the returned records, reducing the query count from 101 to just 2.
21. Practice Exercises
- Easy: Modify the RestSimulator to measure the total payload size of the returned JSON string in bytes, and compare it to the size of a gRPC-style binary payload.
-
Medium: Extend the GraphQlSimulator to support querying a new
emailfield inside the user block, and ensure it is only included in the output JSON if requested by the client query. -
Hard: Build a mock DataLoader class in Java, Python, or C++ that aggregates three individual
GetOrdercalls, delays execution by 50ms, batches the IDs, and retrieves them in a single batch query simulation.
22. Challenge Problem
Problem Statement: Design a movie streaming platform (like Netflix). The system must support two API paths:
1. Client Edge API: Exposes catalog searches, user reviews, and recommendations to mobile, web, and Smart TV applications.
2. Video Playback Telemetry: Handles high-throughput, real-time telemetry from players (buffering events, frame rates, network speeds) every 2 seconds.
Evaluate which protocols (REST, GraphQL, gRPC) are best suited for each path. Sketch the communication flow and serialization formats, and explain how you handle browser client constraints for the telemetry stream.
23. Summary
- REST is resource-centric, cacheable, and widely used for public web APIs.
- GraphQL provides query flexibility, allowing clients to fetch exact data shapes to prevent over-fetching.
- gRPC utilizes binary Protocol Buffers and HTTP/2 multiplexing, making it ideal for microservice communication.
- Most modern systems use GraphQL or REST at the client edge, and gRPC for internal service-to-service calls.
24. Cheat Sheet
| Criteria | REST | GraphQL | gRPC |
|---|---|---|---|
| Design Model | Resource-based (URIs) | Query-based (Graphs) | Service-based (RPC methods) |
| Data Format | JSON / XML / Text | JSON / Text | Protobuf / Binary |
| Transport | HTTP/1.1 or HTTP/2 | HTTP/1.1 or HTTP/2 | HTTP/2 Only |
| Browser Support | Native (Full) | Native (Full) | Requires Proxy (gRPC-Web) |
| Caching | HTTP level (GET endpoints) | Application level (Normalized) | Client level only |
25. Quiz
1. Which API style is most prone to over-fetching and under-fetching issues?
- GraphQL
- REST (Correct)
- gRPC
- WebSockets
Explanation: REST APIs expose fixed-resource endpoints that return pre-defined JSON payloads, which can cause clients to over-fetch unused fields or make multiple calls (under-fetching) to get related resources.
2. Which transport protocol does gRPC require to support multiplexing and streaming?
- HTTP/1.1
- HTTP/2 (Correct)
- SMTP
- UDP
Explanation: gRPC runs on top of HTTP/2 to enable multiplexing, binary framing, bidirectional streaming, and header compression.
3. How does Protocol Buffers (Protobuf) reduce payload size compared to JSON?
- By compressing text into ZIP files.
- By stripping field name keys from payloads, replacing them with integer tags in a binary format. (Correct)
- By deleting duplicate data.
- By converting responses to REST URIs.
Explanation: Protobuf replaces readable text keys (e.g., "username") with small integer tags (e.g., tag 1), creating a highly optimized binary payload.
4. What is the "N+1 Query Problem" in GraphQL?
- A caching bug in public API Gateways.
- Executing 1 query to fetch parent records, followed by $N$ separate database queries to fetch child relationships. (Correct)
- Using more than $N+1$ server threads.
- Exceeding connection pool limits.
Explanation: The N+1 problem occurs when a parent resolver fetches a list of records, and the child resolver executes a database query for each record individually to resolve relationships.
5. How does DataLoader resolve the N+1 problem in GraphQL?
- By converting GraphQL requests to REST calls.
- By batching and caching individual database requests during execution. (Correct)
- By blocking client queries.
- By utilizing gRPC-Web stubs.
Explanation: DataLoader collects requested IDs during resolver execution and batches them into a single query (e.g., using IN), reducing database queries.
6. Why are standard web browsers unable to connect directly to standard gRPC servers?
- Because gRPC is blocked by ISPs.
- Because browsers do not support HTTP/2 protocols at all.
- Because browsers do not expose the low-level socket controls required to handle HTTP/2 frame headers directly. (Correct)
- Because browsers cannot read binary files.
Explanation: Browser APIs do not allow direct control over HTTP/2 frames, requiring a proxy like Envoy to translate gRPC-Web calls to standard gRPC.
7. What format is used to define gRPC API contracts and compile client/server stubs?
- WSDL schemas
- OpenAPI JSON sheets
- Protocol Buffer
.protofiles (Correct) - GraphQL schemas
Explanation: Protocol Buffer (.proto) files serve as gRPC's Interface Definition Language, defining methods and messages for code compilation.
8. Which API paradigm natively supports HTTP-level caching via CDNs?
- REST (using GET requests) (Correct)
- GraphQL (using POST requests)
- gRPC
- WebSockets
Explanation: REST's resource-centric GET requests can be cached natively by CDNs and browsers, whereas GraphQL's POST requests are difficult to cache at the HTTP level.
9. What is a "Query Depth Attack" in GraphQL?
- A DDoS attack on network ports.
- Submitting deeply nested recursive queries that exhaust server CPU and memory resources. (Correct)
- Altering JWT authentication signatures.
- Flooding write database connection pools.
Explanation: A query depth attack exploits GraphQL's relational parsing, submitting deeply nested queries that force the server to execute recursive database queries until it crashes.
10. In a hybrid API architecture, where is gRPC typically used?
- Exposed directly to mobile users.
- Internally for high-speed microservice-to-microservice communication. (Correct)
- To server-side render HTML assets in browsers.
- To fetch public search assets from CDNs.
Explanation: gRPC is ideal for high-throughput, low-latency communication between internal backend microservices, while REST or GraphQL is used for client-facing APIs.
26. Further Reading
- HTTP/2 in Action by Barry Pollard.
- Official gRPC Tutorials and Reference Guides (grpc.io).
- GraphQL official specification sheets (spec.graphql.org).
27. Next Lesson Preview
In the next lesson, we will explore Webhooks, learning how to design real-time, event-driven callback APIs to push updates directly to external client applications.
Key takeaways
- REST uses resource URIs and standard HTTP verbs for simplicity and caching.
- GraphQL allows clients to request precise fields to avoid over/under-fetching.
- gRPC uses Protocol Buffers and HTTP/2 multiplexing for low-latency microservice messaging.