🚪 Single Entry Point
API Gateway provides one door for all clients. Hides microservices complexity.
When you have multiple microservices, clients need to know about each one:
Problems:
Solution: API Gateway - Single entry point!
API Gateway is a single entry point that sits between clients and microservices.
Route requests to appropriate services:
Routing Examples:
| Request | Route To |
|---|---|
GET /api/users/123 | User Service |
POST /api/orders | Order Service |
GET /api/products | Product Service |
POST /api/payments | Payment Service |
Centralized security:
Benefits:
Control request volume per client:
Combine multiple service calls:
Without Gateway (Multiple Requests):
1Client → User Service (get user)2Client → Order Service (get orders)3Client → Product Service (get products)4= 3 round tripsWith Gateway (Aggregation):
1Client → Gateway → [User, Order, Product Services] → Gateway → Client2= 1 round trip1from flask import Flask, request, jsonify2import requests3from functools import wraps4from typing import Dict, Optional5
6app = Flask(__name__)7
8# Service registry9SERVICES = {10 'users': 'http://user-service:8001',11 'orders': 'http://order-service:8002',12 'products': 'http://product-service:8003',13}14
15# Rate limiting (simplified)16request_counts = {}17
18def rate_limit(max_requests=100, window=60):19 """Rate limiting decorator"""20 def decorator(f):21 @wraps(f)22 def wrapper(*args, **kwargs):23 client_id = request.remote_addr24
25 # Reset counter if window expired26 if client_id not in request_counts:27 request_counts[client_id] = {'count': 0, 'reset_at': time.time() + window}28
29 # Check limit30 if request_counts[client_id]['count'] >= max_requests:31 return jsonify({'error': 'Rate limit exceeded'}), 42932
33 # Increment counter34 request_counts[client_id]['count'] += 135
36 return f(*args, **kwargs)37 return wrapper38 return decorator39
40def authenticate():41 """Simple authentication"""42 token = request.headers.get('Authorization')43 if not token or not token.startswith('Bearer '):44 return None45
46 # Validate token (simplified)47 token_value = token.replace('Bearer ', '')48 # In production, validate with auth service49 return {'user_id': '123', 'role': 'user'}50
51def route_to_service(service_name: str, path: str):52 """Route request to appropriate service"""53 service_url = SERVICES.get(service_name)54 if not service_url:55 return None, 40456
57 # Forward request58 url = f"{service_url}{path}"59 method = request.method60 headers = dict(request.headers)61
62 # Remove host header (service will set its own)63 headers.pop('Host', None)64
65 response = requests.request(66 method=method,67 url=url,68 headers=headers,69 data=request.get_data(),70 params=request.args71 )72
73 return response.json(), response.status_code74
75@app.route('/api/users/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])76@rate_limit(max_requests=100)77def users_proxy(path):78 """Proxy requests to user service"""79 user = authenticate()80 if not user:81 return jsonify({'error': 'Unauthorized'}), 40182
83 return route_to_service('users', f'/users/{path}')84
85@app.route('/api/orders/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])86@rate_limit(max_requests=50)87def orders_proxy(path):88 """Proxy requests to order service"""89 user = authenticate()90 if not user:91 return jsonify({'error': 'Unauthorized'}), 40192
93 return route_to_service('orders', f'/orders/{path}')94
95@app.route('/api/user-dashboard/<user_id>')96@rate_limit(max_requests=20)97def user_dashboard(user_id):98 """Aggregate multiple service calls"""99 user = authenticate()100 if not user:101 return jsonify({'error': 'Unauthorized'}), 401102
103 # Call multiple services in parallel104 import concurrent.futures105
106 with concurrent.futures.ThreadPoolExecutor() as executor:107 user_future = executor.submit(108 requests.get,109 f"{SERVICES['users']}/users/{user_id}"110 )111 orders_future = executor.submit(112 requests.get,113 f"{SERVICES['orders']}/orders?userId={user_id}"114 )115
116 user_data = user_future.result().json()117 orders_data = orders_future.result().json()118
119 # Aggregate results120 return jsonify({121 'user': user_data,122 'orders': orders_data123 })124
125if __name__ == '__main__':126 app.run(host='0.0.0.0', port=8000)1import org.springframework.cloud.gateway.route.RouteLocator;2import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;3import org.springframework.context.annotation.Bean;4import org.springframework.http.HttpStatus;5import org.springframework.web.bind.annotation.*;6import reactor.core.publisher.Mono;7
8@RestController9public class ApiGateway {10
11 @Bean12 public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {13 return builder.routes()14 .route("user-service", r -> r15 .path("/api/users/**")16 .filters(f -> f17 .requestRateLimiter(config -> config.setRateLimiter(rateLimiter()))18 .addRequestHeader("X-User-Id", extractUserId())19 )20 .uri("http://user-service:8001"))21 .route("order-service", r -> r22 .path("/api/orders/**")23 .filters(f -> f24 .requestRateLimiter(config -> config.setRateLimiter(rateLimiter()))25 )26 .uri("http://order-service:8002"))27 .build();28 }29
30 @GetMapping("/api/user-dashboard/{userId}")31 public Mono<DashboardResponse> getUserDashboard(@PathVariable String userId) {32 // Aggregate multiple service calls33 Mono<User> user = webClient.get()34 .uri("http://user-service:8001/users/{userId}", userId)35 .retrieve()36 .bodyToMono(User.class);37
38 Mono<List<Order>> orders = webClient.get()39 .uri("http://order-service:8002/orders?userId={userId}", userId)40 .retrieve()41 .bodyToFlux(Order.class)42 .collectList();43
44 return Mono.zip(user, orders)45 .map(tuple -> new DashboardResponse(tuple.getT1(), tuple.getT2()));46 }47
48 private RateLimiter rateLimiter() {49 return RateLimiter.of("default", RateLimiterConfig.of(100, Duration.ofMinutes(1)));50 }51}Just route requests:
1Client → Gateway → ServiceUse when:
Gateway tailored for specific client:
Aggregates multiple service calls:
1Client → Gateway → [Service1, Service2, Service3] → Gateway → ClientUse when:
| Solution | Type | Best For |
|---|---|---|
| Kong | Open source | General purpose, plugin ecosystem |
| AWS API Gateway | Managed | AWS ecosystem |
| Zuul | Open source | Netflix stack, Spring Cloud |
| Envoy | Open source | Service mesh, high performance |
| NGINX | Open source | Simple routing, high performance |
At the code level, gateways translate to routing logic, middleware, and aggregation patterns.
1from abc import ABC, abstractmethod2from typing import Dict, Any, List3
4class Gateway(ABC):5 """Base gateway interface"""6
7 @abstractmethod8 def route(self, request: Dict[str, Any]) -> Dict[str, Any]:9 """Route request to appropriate service"""10 pass11
12 @abstractmethod13 def authenticate(self, request: Dict[str, Any]) -> Optional[Dict[str, Any]]:14 """Authenticate request"""15 pass16
17class APIGateway(Gateway):18 def __init__(self):19 self.routes = {}20 self.middleware = []21
22 def add_route(self, path: str, service: str):23 """Add routing rule"""24 self.routes[path] = service25
26 def add_middleware(self, middleware):27 """Add middleware (auth, rate limiting, etc.)"""28 self.middleware.append(middleware)29
30 def route(self, request: Dict[str, Any]) -> Dict[str, Any]:31 """Route with middleware"""32 # Apply middleware33 for mw in self.middleware:34 request = mw.process(request)35 if request.get('error'):36 return request37
38 # Route to service39 path = request['path']40 service = self.find_service(path)41
42 if not service:43 return {'error': 'Service not found', 'status': 404}44
45 # Forward to service46 return self.forward_to_service(service, request)47
48 def aggregate(self, requests: List[Dict[str, Any]]) -> Dict[str, Any]:49 """Aggregate multiple service calls"""50 results = {}51
52 for req in requests:53 service = self.find_service(req['path'])54 result = self.forward_to_service(service, req)55 results[req['path']] = result56
57 return results1import java.util.List;2import java.util.Map;3import java.util.Optional;4
5interface Gateway {6 Map<String, Object> route(Map<String, Object> request);7 Optional<Map<String, Object>> authenticate(Map<String, Object> request);8}9
10class APIGateway implements Gateway {11 private final Map<String, String> routes;12 private final List<Middleware> middleware;13
14 public APIGateway() {15 this.routes = new HashMap<>();16 this.middleware = new ArrayList<>();17 }18
19 public void addRoute(String path, String service) {20 routes.put(path, service);21 }22
23 public void addMiddleware(Middleware middleware) {24 this.middleware.add(middleware);25 }26
27 public Map<String, Object> route(Map<String, Object> request) {28 // Apply middleware29 for (Middleware mw : middleware) {30 request = mw.process(request);31 if (request.containsKey("error")) {32 return request;33 }34 }35
36 // Route to service37 String path = (String) request.get("path");38 String service = findService(path);39
40 if (service == null) {41 return Map.of("error", "Service not found", "status", 404);42 }43
44 // Forward to service45 return forwardToService(service, request);46 }47
48 public Map<String, Object> aggregate(List<Map<String, Object>> requests) {49 Map<String, Object> results = new HashMap<>();50
51 for (Map<String, Object> req : requests) {52 String service = findService((String) req.get("path"));53 Map<String, Object> result = forwardToService(service, req);54 results.put((String) req.get("path"), result);55 }56
57 return results;58 }59}🚪 Single Entry Point
API Gateway provides one door for all clients. Hides microservices complexity.
🔄 Request Aggregation
Combine multiple service calls into one request. Reduces round trips significantly.
🛡️ Centralized Security
Handle authentication, authorization, and rate limiting in one place. Services stay simple.
⚖️ Load Balancing
Gateway can distribute load across service instances. Services don’t need to know about each other.