System Design Article

gRPC, GraphQL & API Gateway Patterns

Difficulty: Medium

REST is the default API style, but it is not always the best fit. gRPC excels at internal microservice communication with its binary protocol, strong typing, and streaming support. GraphQL solves the over-fetching and under-fetching problems of REST by letting clients request exactly the data they need. API Gateways unify multiple backend services behind a single entry point. This lesson covers when and why to use each technology, how they work at a protocol level, and how to combine them in a real-world architecture.

System Design
/

gRPC, GraphQL & API Gateway Patterns

gRPC, GraphQL & API Gateway Patterns

REST is the default API style, but it is not always the best fit. gRPC excels at internal microservice communication with its binary protocol, strong typing, and streaming support. GraphQL solves the over-fetching and under-fetching problems of REST by letting clients request exactly the data they need. API Gateways unify multiple backend services behind a single entry point. This lesson covers when and why to use each technology, how they work at a protocol level, and how to combine them in a real-world architecture.

System Design
Medium
grpc
graphql
api-gateway
protocol-buffers
microservices
api-design
intermediate

447 views

9

Beyond REST: Why We Need Alternatives

REST is excellent for many use cases, but it has fundamental limitations that emerge at scale:

Problem 1: Over-fetching

A mobile app only needs a user's name and avatar, but GET /users/42 returns 30 fields including address, preferences, and account details. This wastes bandwidth - especially critical on mobile networks.

Problem 2: Under-fetching (N+1 Problem)

To display a user's profile page with their posts and followers, a REST client needs:

Text
1. GET /users/42              (user profile)
2. GET /users/42/posts         (user's posts)
3. GET /users/42/followers     (user's followers)
4. GET /users/15              (author of a comment)
5. GET /users/23              (author of another comment)
... potentially dozens of requests

Each request adds latency. On mobile, this waterfall of requests creates noticeable delays.

Problem 3: JSON and HTTP Overhead

For internal microservice communication with thousands of requests per second:

  • JSON parsing is CPU-intensive compared to binary serialization.
  • HTTP/1.1 headers add hundreds of bytes per request.
  • Text-based protocols are inherently less efficient than binary ones.

Problem 4: No Formal Contract

REST APIs often lack formal schemas. Documentation may be out of date. Different endpoints may use different conventions. This makes client development error-prone.

The Solution: Right Tool for the Right Job

  • gRPC: Binary, strongly typed, multiplexed - ideal for internal service-to-service communication.
  • GraphQL: Flexible queries, single endpoint - ideal for diverse client data needs.
  • API Gateway: Unified entry point that can translate between external (REST/GraphQL) and internal (gRPC) protocols.

gRPC: High-Performance RPC Framework

gRPC (Google Remote Procedure Call) is an open-source RPC framework that uses Protocol Buffers for serialization and HTTP/2 for transport. Originally developed at Google, it is now used by Netflix, Dropbox, Spotify, Square, and many others.

How gRPC Works

1. Define the Service (Protocol Buffers)
Protobuf
// user.proto
syntax = "proto3";

service UserService {
    rpc GetUser (GetUserRequest) returns (User);
    rpc ListUsers (ListUsersRequest) returns (stream User);
    rpc CreateUser (CreateUserRequest) returns (User);
}

message GetUserRequest {
    string user_id = 1;
}

message User {
    string id = 1;
    string name = 2;
    string email = 3;
    int64 created_at = 4;
}

message ListUsersRequest {
    int32 page_size = 1;
    string page_token = 2;
}

message CreateUserRequest {
    string name = 1;
    string email = 2;
}
2. Generate Client and Server Code

The .proto file is compiled into client stubs and server interfaces in your language of choice (Go, Java, Python, C++, Node.js, etc.). The generated code handles serialization, deserialization, and HTTP/2 transport.

3. Call the Service
// Client code (generated from proto)
const response = await userClient.getUser({ userId: "42" });
console.log(response.name); // Strongly typed - IDE autocomplete works

The call looks like a local function call, but it actually sends a binary-encoded request over HTTP/2 to a remote server.

Protocol Buffers: The Serialization Format

FeatureJSON (REST)Protocol Buffers (gRPC)
FormatText (human-readable)Binary (compact)
SizeLarger (field names in every message)3-10x smaller (field numbers, not names)
Parsing speedSlower (text parsing)5-100x faster (binary decoding)
SchemaOptional (OpenAPI)Required (.proto files)
EvolutionFragile (renaming breaks clients)Safe (field numbers are stable)
Human readabilityYesNo (need tools to inspect)

The Four gRPC Streaming Patterns

1. Unary RPC

One request, one response (like REST).

Text
Unary RPC
Client ---request---> Server ---response---> Client

2. Server Streaming

One request, stream of responses. Use case: real-time updates (stock prices, log tailing).

Text
Server Streaming
Client ---request---> Server ---response1---> ---response2---> ---response3---> Client

3. Client Streaming

Stream of requests, one response. Use case: uploading a large file in chunks, sending sensor data.

Text
Client Streaming
Client ---request1---> ---request2---> ---request3---> Server ---response---> Client

4. Bidirectional Streaming

Both sides stream simultaneously. Use case: chat, real-time collaboration.

Text
Bidirectional Streaming
Client <===== stream =====> Server

gRPC Advantages

  • Performance: Binary serialization + HTTP/2 multiplexing = significantly lower latency and bandwidth than REST/JSON.
  • Strong typing: The .proto file is the single source of truth. Generated code catches errors at compile time.
  • Code generation: Client libraries are auto-generated for 10+ languages from a single .proto file.
  • Streaming: Native support for all four streaming patterns.
  • Deadlines and cancellation: Built-in support for request timeouts and cancellation propagation.

gRPC Limitations

  • No browser support: Browsers cannot make gRPC calls directly. You need grpc-web (a proxy that translates between gRPC and HTTP) or an API gateway.
  • Not human-readable: Binary messages cannot be inspected in browser dev tools or curl. Debugging requires special tools (grpcurl, BloomRPC).
  • Learning curve: Protocol Buffers, code generation, and gRPC concepts have a steeper learning curve than REST.
  • Limited caching: gRPC uses HTTP/2 POST for all calls, so HTTP caching (CDNs, proxies) does not work.
  • Smaller ecosystem: REST has decades of tooling (Postman, Swagger, API testing frameworks). gRPC tooling is improving but less mature.

GraphQL: Flexible Client Queries

GraphQL is a query language for APIs developed by Facebook (now Meta) in 2012 and open-sourced in 2015. Instead of multiple REST endpoints, GraphQL exposes a single endpoint where clients specify exactly what data they need.

How GraphQL Works

1. Define the Schema (Server-Side)
Graphql
type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
    followers: [User!]!
    followersCount: Int!
}

type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
    createdAt: DateTime!
    likes: Int!
}

type Query {
    user(id: ID!): User
    users(first: Int, after: String): UserConnection!
    post(id: ID!): Post
}

type Mutation {
    createPost(input: CreatePostInput!): Post!
    updateUser(id: ID!, input: UpdateUserInput!): User!
    deletePost(id: ID!): Boolean!
}

type Subscription {
    newMessage(conversationId: ID!): Message!
}
2. Client Sends a Query
Graphql
# Client requests exactly what it needs
query {
    user(id: "42") {
        name
        avatar
        posts(first: 5) {
            title
            likes
        }
        followersCount
    }
}
3. Server Returns Exactly That Shape
JSON
{
    "data": {
        "user": {
            "name": "Alice",
            "avatar": "https://cdn.example.com/alice.jpg",
            "posts": [
                { "title": "My First Post", "likes": 42 },
                { "title": "GraphQL Deep Dive", "likes": 128 }
            ],
            "followersCount": 1500
        }
    }
}

One request. Exactly the data needed. No over-fetching, no under-fetching.

GraphQL Operations

OperationPurposeREST Equivalent
QueryRead dataGET
MutationWrite/modify dataPOST, PUT, PATCH, DELETE
SubscriptionReal-time updates (via WebSocket)SSE, WebSocket

GraphQL Advantages

  • No over-fetching: Clients request only the fields they need. A mobile client can request fewer fields than a web client from the same endpoint.
  • No under-fetching: A single query can traverse relationships (user -> posts -> comments -> authors) in one request.
  • Strongly typed schema: The schema is the API documentation. Tools like GraphiQL and Apollo Studio provide auto-complete, validation, and documentation from the schema.
  • Versionless: Instead of versioning endpoints (v1, v2), you add new fields and deprecate old ones. Clients only request the fields they use.
  • Introspection: Clients can query the schema itself to discover available types, fields, and operations.

GraphQL Limitations

  • Caching is hard: All GraphQL requests go to a single endpoint (POST /graphql), making HTTP-level caching (CDN, proxy) ineffective. You need application-level caching (Apollo cache, DataLoader).
  • Complexity on the server: Resolvers can create N+1 query problems if not carefully optimized. The DataLoader pattern (batching and caching database queries) is essential.
  • Query complexity attacks: A malicious client can send a deeply nested query that overwhelms the server. You need query depth limiting, complexity analysis, and query cost budgets.
  • File uploads: GraphQL has no native file upload support. Solutions like multipart form requests or separate upload endpoints are workarounds.
  • Error handling: GraphQL always returns HTTP 200 (even for errors), with errors in the response body. This complicates monitoring and HTTP-level error tracking.

N+1 Problem in GraphQL

Graphql
query {
    posts(first: 20) {    # 1 query to fetch 20 posts
        title
        author {           # 20 separate queries to fetch each author!
            name
        }
    }
}

Solution: DataLoader batches the 20 individual author queries into a single SELECT * FROM users WHERE id IN (1, 2, 3, ...) query.

Without DataLoader, this simple query generates 21 database queries. With DataLoader, it generates 2.

API Gateway Patterns

An API Gateway is a server that acts as the single entry point for all client requests. It sits between clients and backend services, handling cross-cutting concerns.

Why Use an API Gateway?

In a microservice architecture, clients would otherwise need to know the address of every service:

Text
# Without API Gateway (clients talk to each service directly)
Mobile App -> User Service:   https://users.internal:8080
Mobile App -> Order Service:  https://orders.internal:8081
Mobile App -> Payment Service: https://payments.internal:8082
Mobile App -> Search Service: https://search.internal:8083

Problems: Clients need to know about every service, handle authentication with each one, and make multiple requests. Internal service addresses are exposed.

Text
# With API Gateway (single entry point)
Mobile App -> API Gateway: https://api.example.com
              -> routes to User Service, Order Service, etc.

API Gateway Responsibilities

ResponsibilityDescription
Request routingRoutes requests to the appropriate backend service based on URL path, headers, or query parameters
Authentication & authorizationValidates JWT tokens, API keys, or OAuth credentials before forwarding requests
Rate limitingEnforces request quotas per client, API key, or IP address
Load balancingDistributes requests across multiple instances of a service
Request/response transformationTranslates between protocols (REST to gRPC), formats (JSON to Protobuf), or API versions
CachingCaches responses for identical requests to reduce backend load
Circuit breakingStops sending requests to a failing service, returning a fallback response
Logging & monitoringCentralizes request logging, metrics, and distributed tracing
SSL terminationHandles HTTPS encryption, offloading TLS from backend services
CORS handlingManages Cross-Origin Resource Sharing headers for browser clients

API Gateway Patterns

1. Simple Routing Gateway

Routes requests based on URL path to the correct backend service.

Text
/api/v1/users/*    -> User Service
/api/v1/orders/*   -> Order Service
/api/v1/products/* -> Product Service
2. Backend for Frontend (BFF)

Separate gateways optimized for each client type.

Text
[Mobile App] -> [Mobile BFF Gateway] -> Backend Services
[Web App]    -> [Web BFF Gateway]    -> Backend Services
[Partner API] -> [Partner Gateway]   -> Backend Services

Each BFF aggregates data differently based on what its client needs. The mobile BFF returns smaller payloads; the web BFF returns richer data.

3. GraphQL Gateway

A GraphQL server acts as the API gateway, aggregating data from multiple backend services.

Text
[Client] -> [GraphQL Gateway] -> User Service (gRPC)
                               -> Order Service (gRPC)
                               -> Product Service (REST)

The GraphQL gateway resolves each field by calling the appropriate backend service and assembles the response.

Popular API Gateway Solutions

SolutionTypeKey Features
AWS API GatewayManagedREST & WebSocket APIs, Lambda integration, usage plans
KongOpen-sourcePlugin architecture, rate limiting, authentication, gRPC support
EnvoyOpen-source proxygRPC-native, service mesh integration, advanced load balancing
NGINXOpen-sourceReverse proxy, SSL termination, rate limiting
Apollo GatewayGraphQLSchema federation, query planning, caching

REST vs gRPC vs GraphQL: Comprehensive Comparison

Feature Comparison

FeatureRESTgRPCGraphQL
ProtocolHTTP/1.1 or HTTP/2HTTP/2 (required)HTTP (typically POST)
Data formatJSON (text)Protocol Buffers (binary)JSON (text)
SchemaOptional (OpenAPI)Required (.proto)Required (SDL)
Type safetyWeak (runtime)Strong (compile-time)Strong (schema validation)
Endpoint modelMultiple endpoints (/users, /posts)Multiple services/methodsSingle endpoint (/graphql)
Fetching flexibilityFixed response shapeFixed response shapeClient specifies fields
StreamingSSE (server push)4 streaming patternsSubscriptions (via WS)
Browser supportNativegrpc-web proxy requiredNative
CachingExcellent (HTTP cache)Difficult (POST-based)Difficult (POST-based)
File uploadNative (multipart)StreamingNot native (workarounds)
Payload sizeMedium-LargeSmall (3-10x smaller)Medium (depends on query)
LatencyMediumLowMedium
Code generationOptionalRequired (from .proto)Optional (from schema)
Learning curveLowHighMedium
DebuggingEasy (text, curl)Hard (binary, special tools)Medium (single endpoint)

When to Use Each

Use REST When:
  • Building a public-facing API consumed by third-party developers
  • Your API is CRUD-heavy with simple data relationships
  • HTTP caching is important (CDN, browser cache)
  • You want the simplest possible API with the lowest learning curve
  • Your clients are primarily web browsers
Use gRPC When:
  • Building internal microservice-to-microservice communication
  • Low latency and high throughput are critical
  • You need streaming (server streaming, bidirectional streaming)
  • Multiple teams are building services in different programming languages (gRPC's code generation ensures consistency)
  • You want strong typing with compile-time error checking
Use GraphQL When:
  • You have multiple client types (mobile, web, desktop) with different data needs
  • Your data has complex relationships that require fetching from multiple sources
  • Over-fetching is a measurable problem (especially on mobile)
  • You want a self-documenting API with built-in introspection
  • Your frontend team wants to iterate quickly without backend changes

The Hybrid Approach (Most Common in Production)

Text
[Web/Mobile Clients] --REST or GraphQL--> [API Gateway]
                                            |
                                  [API Gateway routes to:]
                                     /          |          \
                              [Service A]  [Service B]  [Service C]
                                  <----gRPC between services---->
  • External: REST or GraphQL (browser-compatible, developer-friendly)
  • Internal: gRPC (high performance, strongly typed)
  • API Gateway: Translates between external and internal protocols

Real-World Trade-offs & Pitfalls

Trade-off 1: Flexibility vs Predictability

GraphQL gives clients maximum flexibility but makes server-side optimization harder. The server cannot predict or cache query results because every query is different.

gRPC gives the server maximum control over response shape but requires clients to accept the predefined structure.

REST is in the middle: predictable response shapes that are easy to cache but may not fit every client's needs.

Trade-off 2: Performance vs Developer Experience

gRPC has the best raw performance (binary encoding, HTTP/2) but the worst developer experience for debugging and testing.

REST has the best developer experience (curl, Postman, browser) but mediocre performance for high-throughput internal calls.

GraphQL has good developer experience (GraphiQL, introspection) but requires careful optimization (DataLoader, query complexity limits) to avoid performance pitfalls.

Trade-off 3: Simplicity vs Power

Adding gRPC or GraphQL to your stack introduces operational complexity:

  • gRPC: Proto file management, code generation pipelines, grpc-web proxy for browser clients.
  • GraphQL: Schema management, resolver optimization, query complexity defense, custom caching layer.
  • REST: Simplest to deploy, monitor, and debug. Every tool in the ecosystem supports it.

Pitfall: GraphQL as a Database Query Language

GraphQL is an API query language, not a database query language. The schema should represent your business domain, not your database tables. Exposing raw database access through GraphQL is a security and performance nightmare.

Pitfall: gRPC for Everything

gRPC is not suitable for public APIs (no browser support), simple CRUD apps (overkill), or scenarios where HTTP caching matters. It shines specifically for internal service communication at scale.

Pitfall: API Gateway as a Monolith

If your API gateway contains business logic, it becomes a distributed monolith - a single point of failure that every team depends on. Keep the gateway thin: routing, authentication, rate limiting. Business logic belongs in the backend services.

Using gRPC, GraphQL & API Gateways in Interviews

When to Introduce These in an Interview

API Gateway: Mention it in every microservice design. "All client requests go through our API Gateway, which handles authentication, rate limiting, and routes to the appropriate backend service."

gRPC: Mention when you draw service-to-service arrows. "Backend services communicate via gRPC for lower latency and strong typing. The API gateway translates between REST (external) and gRPC (internal)."

GraphQL: Mention when the interviewer describes diverse clients or complex data fetching. "Since we have a mobile app that needs minimal data and a web app that needs rich data, I would use GraphQL to let each client request exactly what it needs."

Example: "Design Instagram"

Text
[Mobile App / Web App]
        |
        | GraphQL (clients request exactly the fields they need)
        v
[API Gateway / GraphQL Server]
        |          |          |          |
        | gRPC     | gRPC     | gRPC     | gRPC
        v          v          v          v
  [User Svc]  [Post Svc]  [Feed Svc]  [Media Svc]

Why GraphQL externally: Mobile clients need different data than web clients. The feed page, profile page, and explore page all need different combinations of user, post, and media data. GraphQL lets each client request exactly what it needs.

Why gRPC internally: The services communicate at high frequency. gRPC's binary encoding and HTTP/2 multiplexing reduce latency. The .proto schema ensures all services agree on data formats.

Why API Gateway: Centralizes authentication (JWT validation), rate limiting (per-user quotas), and provides a single entry point for all clients.

Quick Interview Phrases

  • "For external clients, REST is our default. If we need flexible data fetching for mobile and web, GraphQL."
  • "Internal services communicate via gRPC for performance and type safety."
  • "The API Gateway handles cross-cutting concerns: auth, rate limiting, SSL termination, and request routing."
  • "We use the Backend for Frontend pattern: separate BFF gateways for mobile and web to optimize response payloads."
  • "The GraphQL layer uses DataLoader to batch and deduplicate database queries, avoiding the N+1 problem."

Real-World Examples

How real systems implement this in production

Netflix

Netflix uses a GraphQL-based API gateway (Federated GraphQL) for its client-facing API, allowing mobile, TV, and web clients to request exactly the data they need. Backend services communicate via gRPC. Their custom gateway, Zuul, handles routing, authentication, and canary deployments.

Trade-off: GraphQL federation allows teams to independently develop and deploy their portion of the graph, but requires coordination on schema design and introduces complexity in query planning across federated services.

Shopify

Shopify offers both REST and GraphQL APIs to third-party developers. Their GraphQL API allows complex queries across products, orders, and customers in a single request, reducing the number of API calls merchants need to make. Internally, services communicate via gRPC.

Trade-off: Maintaining both REST and GraphQL APIs doubles the API surface area but serves different developer needs: REST for simple integrations, GraphQL for complex storefront customization.

Uber

Uber uses gRPC extensively for internal microservice communication (thousands of services). Their API gateway, Peloton, translates between REST (for mobile clients) and gRPC (for backend services). Protocol Buffers serve as the shared schema across all services.

Trade-off: gRPC provides strong contracts between hundreds of microservice teams, but the proto file management, code generation pipelines, and breaking change detection require dedicated tooling and infrastructure.

Quick Interview Phrases

Key terms to use in your answer

Protocol Buffers schema
bidirectional streaming
N+1 query problem
DataLoader batching
schema-first design
Backend for Frontend (BFF)

Common Interview Questions

Questions you might be asked about this topic

gRPC for internal microservice communication - binary protocol is faster, streaming support, strong typing via Protobuf. REST for external/public APIs - browser-compatible, human-readable, wider tooling support.

Interview Tips

How to discuss this topic effectively

1

Default to REST for external APIs and gRPC for internal services. This is the most common production pattern and interviewers expect it. If you can justify GraphQL for your specific use case (diverse clients, complex data), even better.

2

Always include an API Gateway in microservice designs. Even a brief mention - 'All requests go through the API gateway, which handles auth and rate limiting' - signals architectural awareness.

3

When proposing GraphQL, proactively address the N+1 problem: 'The GraphQL layer uses DataLoader to batch database queries and avoid the N+1 problem.' This shows you know the pitfalls, not just the benefits.

4

Know that gRPC requires HTTP/2 and binary encoding, which means browsers cannot call gRPC directly. Always mention the grpc-web proxy or API gateway translation for browser clients.

5

When drawing architecture diagrams, label the protocol on each arrow: 'REST', 'gRPC', 'GraphQL', 'WebSocket'. This shows you are deliberate about protocol choices rather than assuming HTTP everywhere.

6

If the interviewer asks about API evolution, mention that GraphQL is versionless (add new fields, deprecate old ones) while REST requires URL versioning (/v1/, /v2/). This is a nuanced advantage of GraphQL.

Common Mistakes

Pitfalls to avoid in interviews

Proposing gRPC for a public-facing API used by web browsers

Browsers cannot make native gRPC calls (HTTP/2 trailers are not supported). For browser clients, use REST or GraphQL. gRPC is best suited for internal service-to-service communication. If you must expose gRPC to browsers, use grpc-web with a proxy, but REST or GraphQL is usually simpler.

Using GraphQL without addressing the N+1 query problem

GraphQL resolvers execute independently, which can generate N+1 database queries (1 query for the list + N queries for each item's related data). Always mention DataLoader or batching to solve this. Without it, a single GraphQL query can generate hundreds of database queries.

Putting business logic in the API Gateway

The API Gateway should be a thin layer handling cross-cutting concerns: routing, authentication, rate limiting, SSL termination. If you add business logic (order validation, price calculation), the gateway becomes a distributed monolith that every team depends on and no team owns.

Assuming all three styles (REST, gRPC, GraphQL) are interchangeable alternatives

They serve different purposes and excel in different contexts. In a well-designed system, you might use all three: REST for third-party APIs, GraphQL for flexible client queries, and gRPC for internal services. They complement each other rather than competing.