Metadata
| Field | Value |
|---|---|
| Slug | api-gateway |
| Difficulty | Intermediate |
| Estimated Reading Time | 12 min |
| Estimated Coding Time | 20 min |
| Tier | 1 — Core Backend Components |
| Implementation Language | Node.js (Express) |
| SEO Description | Learn 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:
- It has to make 3 separate network round-trips over a slow mobile network.
- It has to know the exact IP/DNS of all 3 services.
- 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 ServiceintoPaymentandShipping, 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
/usersto the User Service, and/ordersto 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
| System | Tool | Use Case |
|---|---|---|
| AWS Ecosystem | Amazon API Gateway | Front door for AWS Lambda functions |
| Netflix | Zuul / GraphQL Federation | Aggregating data from 100+ microservices for the UI |
| Kubernetes | Ingress Controllers (Nginx, Traefik) | Routing external traffic into the cluster |
| Open Source | Kong, Tyk, KrakenD | Standalone API Gateways |
4. Prerequisites
| Concept | Block |
|---|---|
| REST API Design | 002 REST API Design |
| Reverse Proxy concepts | 007 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:
- Asynchronously fires requests to User, Order, and Product services.
- Waits for all of them to return (or timeout).
- Merges the JSON responses into a single payload.
- 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
| Metric | Details |
|---|---|
| Latency Overhead | Adds ~2-10ms per request (due to JWT parsing and network hop). |
| Aggregation Speed | Bound by the slowest downstream service. If User Service takes 10ms and Order Service takes 2000ms, the Dashboard endpoint takes 2000ms. |
| Scalability | Gateways 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
| Pros | Cons |
|---|---|
| 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
| Concern | This Implementation | Production Systems (Kong / AWS API Gateway) |
|---|---|---|
| Routing Config | Hardcoded inside code | Dynamic routing maps stored in a database (e.g., PostgreSQL or etcd), updated without restarting the Gateway. |
| Aggregation | Custom Node.js code | GraphQL Federation: Clients write a GraphQL query, and the Gateway automatically fans it out to REST/gRPC backends. |
| Service Discovery | Hardcoded localhost:8081 | Gateway integrates with Consul/Eureka (Block 004) to dynamically resolve IPs. |
| Rate Limiting | None | Distributed Rate Limiting via Redis (Block 009) to prevent abuse. |
11. Common Bugs
| Bug | What happens | Fix |
|---|---|---|
| The "Slow Backend" Problem | One 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 Gateway | An 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 Gateway | Developers 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
-
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.
-
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. -
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.
-
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)
| System | Gateway Strategy |
|---|---|
| Netflix | Invented 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). |
| Uber | Uses an API Gateway to authenticate riders/drivers and route requests to thousands of internal services. |
15. Related Blocks
| Relationship | Block |
|---|---|
| Previous | 007 Load Balancer |
| Next | 009 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
| Field | Value |
|---|---|
| Hero Title | API Gateway |
| Hero Subtitle | The front door to your microservices. Centralize authentication, routing, and rate limiting. |
| Breadcrumb | System Design → Building Blocks → API Gateway |
| Sidebar Category | Tier 1 — Core Backend Components |
| Search Keywords | api gateway, microservices, bff, backend for frontend, request aggregation, authentication offloading, jwt, reverse proxy |
| Internal Links | ← 007 Load Balancer · → 009 Rate Limiter |
| Suggested Illustration | A bouncer (Gateway) checking IDs at the door of a club, then directing people to different rooms (Bar, Dancefloor, VIP) based on their wristbands. |
| Suggested Animation | A 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. |