SSourav Saha
HomeExperienceSoftware DesignSystem DesignLearningBooksToolsContact
SSourav Saha

Building scalable backend systems, distributed infrastructure, and cloud-native applications.

Navigation

  • Home
  • Experience
  • Software Design
  • System Design
  • Learning

More

  • Books
  • Tools
  • Contact

Connect

  • LinkedIn
  • Email

© 2026 Sourav Saha. All rights reserved.

Built with using Next.js

System DesignAPI Gateway
System Designintermediate

API Gateway

The front door to your microservices. Understand how API Gateways handle routing, authentication, rate limiting, and protocol translation, with a runnable Node.js implementation.

January 8, 202412 min read
api-gatewaymicroservicesauthenticationroutingbuilding-blocktier-1

Metadata

FieldValue
Slugapi-gateway
DifficultyIntermediate
Estimated Reading Time12 min
Estimated Coding Time20 min
Tier1 — Core Backend Components
Implementation LanguageNode.js (Express)
SEO DescriptionLearn the API Gateway pattern for microservices. Understand request routing, authentication offloading, rate limiting, and build a custom API Gateway in Node.js.

1. Overview

What problem does it solve?

In a monolith, clients talk to one server. In a microservices architecture, a mobile app might need data from the User Service, Order Service, and Product Service just to render the home screen.

If the mobile app talks directly to these services:

  1. It has to make 3 separate network round-trips over a slow mobile network.
  2. It has to know the exact IP/DNS of all 3 services.
  3. Every single service has to independently verify the user's JWT token.

An API Gateway sits between the clients and the microservices. It acts as a Reverse Proxy, providing a single, unified API entry point, handling cross-cutting concerns (Auth, Rate Limiting) so the backend services don't have to.

What breaks without it?

  • Chatty Clients: Mobile apps drain battery making dozens of HTTP requests instead of one aggregated request.
  • Security Nightmares: Every microservice must implement JWT validation perfectly. If one forgets, you have a data breach.
  • Coupling: If you split the Order Service into Payment and Shipping, you have to update the mobile app.

2. Motivation

API Gateway vs Load Balancer

A common interview mistake is confusing an API Gateway with a Load Balancer (Block 007).

  • A Load Balancer distributes traffic across identical instances of the same service (e.g., sending traffic to Server A or Server B).
  • An API Gateway routes traffic to different services based on the URL or headers (e.g., sending /users to the User Service, and /orders to the Order Service).

In modern architectures (like AWS API Gateway + ALB, or Nginx), these two roles are often combined into a single piece of infrastructure.

The BFF Pattern (Backend for Frontend)

Instead of one massive API Gateway for everything, companies often build specific gateways tailored for specific clients.

  • One Gateway for the iOS App (optimized for small payloads).
  • One Gateway for the Web Dashboard (optimized for complex analytical queries).

3. Real-World Usage

SystemToolUse Case
AWS EcosystemAmazon API GatewayFront door for AWS Lambda functions
NetflixZuul / GraphQL FederationAggregating data from 100+ microservices for the UI
KubernetesIngress Controllers (Nginx, Traefik)Routing external traffic into the cluster
Open SourceKong, Tyk, KrakenDStandalone API Gateways

4. Prerequisites

ConceptBlock
REST API Design002 REST API Design
Reverse Proxy concepts007 Load Balancer

5. Visual Explanation

The API Gateway Pattern

graph TD
    Client["Mobile App"] --> |Single Request: GET /home| GW{"API Gateway"}
    
    subgraph "Cross-Cutting Concerns"
        GW_Auth["1. Auth / JWT Validation"]
        GW_RL["2. Rate Limiting"]
        GW_Rout["3. Routing / Aggregation"]
    end
    
    GW -.-> GW_Auth -.-> GW_RL -.-> GW_Rout
    
    GW_Rout --> |GET /users/me| US["User Service"]
    GW_Rout --> |GET /orders/recent| OS["Order Service"]
    GW_Rout --> |GET /recommendations| RS["Rec Service"]
    
    style GW fill:#f9f,stroke:#333,stroke-width:2px

Protocol Translation

Sometimes internal services don't use HTTP. An API Gateway can accept JSON/HTTP from a web browser, and translate it into gRPC or AMQP (Message Queues) for the internal backend, completely hiding the internal complexity from the client.


6. Internal Working

6.1 Authentication Offloading

Instead of every microservice validating JWT signatures (which requires every service to have access to public keys/secrets), the API Gateway validates the token once.

If the token is valid, the Gateway decrypts the user_id and injects it into an HTTP header (e.g., X-User-Id: 42). The internal microservices blindly trust this header, assuming that if a request reached them, the Gateway already verified it. (Note: This requires strict network boundaries so attackers cannot bypass the gateway).

6.2 Request Aggregation (Scatter-Gather)

Instead of the client making three requests, it makes one to the Gateway: GET /dashboard. The Gateway:

  1. Asynchronously fires requests to User, Order, and Product services.
  2. Waits for all of them to return (or timeout).
  3. Merges the JSON responses into a single payload.
  4. Returns it to the client.

6.3 Cross-Cutting Concerns

Gateways handle anything that applies to all services:

  • Rate Limiting: "User 42 can only make 100 requests per minute globally."
  • CORS: Handling Cross-Origin Resource Sharing headers for web browsers.
  • SSL Termination: Decrypting HTTPS into HTTP.
  • Caching: Returning cached responses for common GET requests without hitting backends.

7. Implementation

Why Node.js? Because API Gateways perform highly concurrent, I/O-heavy operations (waiting for multiple backend services to respond), Node's single-threaded event loop and async/await pattern are a perfect fit for building lightweight gateways.

/**
 * 008 - API Gateway Implementation (Node.js/Express)
 * Demonstrates Authentication Offloading, Request Routing, and Aggregation.
 * Run: `npm init -y && npm install express axios jsonwebtoken`
 *      `node gateway.js`
 */
const express = require('express');
const axios = require('axios');
const jwt = require('jsonwebtoken');

const app = express();
app.use(express.json());

const JWT_SECRET = "super_secret_key"; // In prod, load from KMS/Env

// ── 1. Authentication Middleware ──

const authenticate = (req, res, next) => {
    const authHeader = req.headers['authorization'];
    if (!authHeader) {
        return res.status(401).json({ error: "Missing Authorization header" });
    }

    const token = authHeader.split(' ')[1]; // Bearer <token>
    
    try {
        // Validate the JWT
        const decoded = jwt.verify(token, JWT_SECRET);
        
        // Inject the verified User ID into headers for downstream services
        req.headers['x-user-id'] = decoded.userId;
        req.headers['x-user-role'] = decoded.role;
        
        next(); // Pass to router
    } catch (err) {
        return res.status(403).json({ error: "Invalid or expired token" });
    }
};

// ── 2. Simple Routing (Reverse Proxy) ──

// Route all /api/users traffic to the User Service
app.use('/api/users', authenticate, async (req, res) => {
    try {
        const userServiceUrl = `http://localhost:8081${req.url}`;
        
        const response = await axios({
            method: req.method,
            url: userServiceUrl,
            data: req.body,
            headers: {
                // Forward the trusted user ID to the microservice
                'x-user-id': req.headers['x-user-id']
            }
        });
        
        res.status(response.status).json(response.data);
    } catch (error) {
        // Handle backend failures
        if (error.response) {
            res.status(error.response.status).json(error.response.data);
        } else {
            res.status(502).json({ error: "User Service is down" });
        }
    }
});

// ── 3. Request Aggregation (BFF Pattern) ──

// A custom endpoint that fetches data from multiple services at once
app.get('/api/dashboard', authenticate, async (req, res) => {
    const userId = req.headers['x-user-id'];
    
    try {
        // Scatter: Fetch from multiple services concurrently
        const [userResp, ordersResp] = await Promise.all([
            // In a real app, these hit the actual microservices
            // axios.get(`http://localhost:8081/users/${userId}`),
            // axios.get(`http://localhost:8082/orders?userId=${userId}`)
            
            // Mocking the responses for demonstration
            Promise.resolve({ data: { id: userId, name: "Sourav" } }),
            Promise.resolve({ data: [{ orderId: 101, total: 50.00 }] })
        ]);

        // Gather: Combine the responses
        const aggregatedResponse = {
            user_profile: userResp.data,
            recent_orders: ordersResp.data,
            timestamp: new Date().toISOString()
        };

        res.json(aggregatedResponse);
    } catch (error) {
        res.status(500).json({ error: "Failed to aggregate dashboard data" });
    }
});

// ── Dev Helper: Generate a JWT ──
app.get('/dev/token', (req, res) => {
    const token = jwt.sign({ userId: 42, role: "admin" }, JWT_SECRET, { expiresIn: '1h' });
    res.json({ token });
});

app.listen(3000, () => {
    console.log("API Gateway running on port 3000");
    console.log("1. Get a token: GET http://localhost:3000/dev/token");
    console.log("2. Fetch dashboard: GET http://localhost:3000/api/dashboard");
});

Sample Output

# 1. Get a token
$ curl http://localhost:3000/dev/token
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}

# 2. Hit the aggregated endpoint without a token (Fails!)
$ curl http://localhost:3000/api/dashboard
{"error":"Missing Authorization header"}

# 3. Hit the aggregated endpoint WITH the token
$ curl -H "Authorization: Bearer eyJhbGciOiJI..." http://localhost:3000/api/dashboard | python -m json.tool
{
    "user_profile": {
        "id": 42,
        "name": "Sourav"
    },
    "recent_orders": [
        {
            "orderId": 101,
            "total": 50.0
        }
    ],
    "timestamp": "2024-06-15T10:00:00.000Z"
}

8. Complexity

MetricDetails
Latency OverheadAdds ~2-10ms per request (due to JWT parsing and network hop).
Aggregation SpeedBound by the slowest downstream service. If User Service takes 10ms and Order Service takes 2000ms, the Dashboard endpoint takes 2000ms.
ScalabilityGateways are stateless (JWTs hold state, not the server). You can scale them horizontally by putting an L4 Load Balancer in front of multiple API Gateways.

9. Trade-offs

ProsCons
Simplified Clients: Clients make fewer requests; don't need to know internal IPs.Single Point of Failure: If the Gateway dies, the entire system is down.
Security: Centralized Auth and Rate Limiting.Deployment Bottleneck: If every team needs to update the Gateway to expose a new route, it slows down development.
Decoupling: You can split/merge microservices without changing client code.Increased Latency: Every request requires an extra network hop.

10. Production Evolution

ConcernThis ImplementationProduction Systems (Kong / AWS API Gateway)
Routing ConfigHardcoded inside codeDynamic routing maps stored in a database (e.g., PostgreSQL or etcd), updated without restarting the Gateway.
AggregationCustom Node.js codeGraphQL Federation: Clients write a GraphQL query, and the Gateway automatically fans it out to REST/gRPC backends.
Service DiscoveryHardcoded localhost:8081Gateway integrates with Consul/Eureka (Block 004) to dynamically resolve IPs.
Rate LimitingNoneDistributed Rate Limiting via Redis (Block 009) to prevent abuse.

11. Common Bugs

BugWhat happensFix
The "Slow Backend" ProblemOne downstream service (Orders) is slow. The Gateway uses all its threads waiting for Orders, causing all endpoints (even Users) to hang.Implement Timeouts and Circuit Breakers (Block 028) on every outgoing HTTP call.
Bypassing the GatewayAn internal service accidentally exposes its port to the public internet, allowing attackers to hit it without a JWT.Network segmentation (VPCs). Microservices should only accept traffic originating from the Gateway's IP address.
Fat GatewayDevelopers put business logic (e.g., calculating tax) into the Gateway because it's easier. The Gateway becomes a monolithic bottleneck.Strictly enforce that the Gateway only handles routing and cross-cutting concerns. No business logic!

12. Interview Questions

  1. How is an API Gateway different from an L7 Load Balancer? Hint: An L7 LB routes to identical instances of the same service. A Gateway routes to entirely different services and handles cross-cutting concerns like JWT validation.

  2. In an API Gateway architecture, how do downstream microservices know who the user is? Hint: The Gateway validates the JWT, extracts the User ID, and injects it into an HTTP header (like X-User-Id). Downstream services trust this header.

  3. What is the Backend-For-Frontend (BFF) pattern and why use it? Hint: Instead of one massive API Gateway, you build specific gateways for specific clients (iOS BFF, Web BFF). It prevents the Gateway from becoming a bloated monolith.

  4. Your API Gateway aggregates data from Service A (fast) and Service B (slow). How do you prevent Service B from taking down the whole Gateway? Hint: Use a Circuit Breaker. If Service B times out, the Gateway returns fallback data (or partial data) instead of holding the connection open.


13. Used By (Downstream Blocks)

  • 009 Rate Limiter — Rate limiting logic is usually executed directly inside the API Gateway.
  • 028 Circuit Breaker — Gateways wrap outgoing requests in circuit breakers to handle downstream failures.
  • 035 Observability — The Gateway is the best place to inject Distributed Tracing IDs (Zipkin/Jaeger) into incoming requests.

14. Used In (Case Studies)

SystemGateway Strategy
NetflixInvented Zuul to handle massive device fragmentation (hundreds of TV models, phones, browsers). Later migrated to GraphQL Federation.
Amazon (AWS)Amazon API Gateway provides a managed front-door for serverless architectures (Lambda).
UberUses an API Gateway to authenticate riders/drivers and route requests to thousands of internal services.

15. Related Blocks

RelationshipBlock
Previous007 Load Balancer
Next009 Rate Limiter

16. Try It Yourself

Exercise 1: Implement Timeouts

In the axios request to the User Service, add a timeout of 500ms. If the request takes longer than 500ms, catch the error and return a 504 Gateway Timeout status code to the client.

Exercise 2: Role-Based Routing

Modify the Authentication Middleware. If a user tries to access /api/admin, check the x-user-role header. If the role is not "admin", return a 403 Forbidden directly from the Gateway without hitting the backend service.


Website Metadata

FieldValue
Hero TitleAPI Gateway
Hero SubtitleThe front door to your microservices. Centralize authentication, routing, and rate limiting.
BreadcrumbSystem Design → Building Blocks → API Gateway
Sidebar CategoryTier 1 — Core Backend Components
Search Keywordsapi gateway, microservices, bff, backend for frontend, request aggregation, authentication offloading, jwt, reverse proxy
Internal Links← 007 Load Balancer · → 009 Rate Limiter
Suggested IllustrationA bouncer (Gateway) checking IDs at the door of a club, then directing people to different rooms (Bar, Dancefloor, VIP) based on their wristbands.
Suggested AnimationA mobile phone sends one request. The Gateway catches it, validates a lock icon (JWT), splits the request into three paths, waits for them to return, zips them together, and sends them back.
PreviousRate LimiterNextLoad Balancer