API Gateway Patterns — Kong, AWS, Rate Limiting, and Routing
In this tutorial, you'll learn about API Gateway Patterns. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
An API Gateway is a reverse Proxy that sits between clients and backend services, handling request routing, authentication, Rate Limiting, logging, and response transformation as a single entry point for all API traffic.
What You'll Learn
By the end of this tutorial, you will understand API Gateway architecture, how to configure Kong and AWS API Gateway for routing and Rate Limiting, and how gateways improve security and Observability in microservice deployments.
Why It Matters
Without an API Gateway, every client must know the location of every backend service, authentication logic is duplicated across services, and cross-cutting concerns like Rate Limiting and logging are inconsistently applied. Doda Browser uses an API Gateway to route search queries to the indexing service, authentication requests to the auth service, and file analysis to the malware scanning microservice.
Real-World Use
A typical e-commerce platform routes /products to the product service, /orders to the order service, and /auth to the authentication service through a single gateway that enforces rate limits, logs every request, and aggregates responses.
API Gateway Architecture
flowchart TB
Client[Client Apps] --> GW[API Gateway]
GW --> Auth[Auth Service]
GW --> Product[Product Service]
GW --> Order[Order Service]
GW --> Payment[Payment Service]
GW --> RateLimit[Rate Limiter]
GW --> Cache[Response Cache]
style GW fill:#22c55e,color:#fff
Kong API Gateway Configuration
Kong is an open-source API Gateway built on NGINX. It uses plugins for authentication, Rate Limiting, and logging.
# Start Kong with Docker
Docker network create kong-net
Docker run -d --name kong-database \
--network=kong-net \
-p 5432:5432 \
-e POSTGRES_DB=kong \
-e POSTGRES_USER=kong \
-e POSTGRES_PASSWORD=kong \
postgres:16
Docker run -d --name kong \
--network=kong-net \
-e KONG_DATABASE=postgres \
-e KONG_PG_HOST=kong-database \
-e KONG_PG_USER=kong \
-e KONG_PG_PASSWORD=kong \
-e KONG_Proxy_ACCESS_LOG=/dev/stdout \
-e KONG_ADMIN_ACCESS_LOG=/dev/stdout \
-e KONG_Proxy_ERROR_LOG=/dev/stderr \
-e KONG_ADMIN_ERROR_LOG=/dev/stderr \
-e KONG_ADMIN_LISTEN=0.0.0.0:8001 \
-p 8000:8000 \
-p 8443:8443 \
-p 8001:8001 \
-p 8444:8444 \
kong:3.7
Expected behavior: Kong starts with Proxy on port 8000 and admin API on port 8001. You can verify with curl HTTP://localhost:8001/status.
# Register a backend service
curl -s -X POST HTTP://localhost:8001/services \
--data name=product-service \
--data URL=HTTP://product-API:3000
# Create a route for the service
curl -s -X POST HTTP://localhost:8001/services/product-service/routes \
--data paths[]=/products \
--data name=product-route
# Enable Rate Limiting plugin
curl -s -X POST HTTP://localhost:8001/plugins \
--data name=rate-limiting \
--data config.minute=100 \
--data config.policy=local
Expected output: The service is registered, route is created, and Rate Limiting is set to 100 requests per minute. Requests to /products are forwarded to the product service, and exceeding 100 requests per minute returns HTTP 429.
AWS API Gateway with Lambda Integration
AWS API Gateway handles HTTP traffic and integrates with Lambda functions, DynamoDB, and other AWS services.
# lambda_handler.py
# AWS Lambda function behind API Gateway
import JSON
import boto3
import os
from datetime import datetime
DynamoDB = boto3.resource('DynamoDB')
table = DynamoDB.Table(os.environ['PRODUCTS_TABLE'])
def lambda_handler(event, context):
"""Handle CRUD operations routed through API Gateway."""
HTTP_method = event['httpMethod']
path = event['path']
path_params = event.get('pathParameters') or {}
print(f"Received {HTTP_method} {path} at {datetime.now().isoformat()}")
if HTTP_method == 'GET' and path == '/products':
response = table.scan()
items = response.get('Items', [])
print(f"Returning {len(items)} products")
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/JSON'},
'body': JSON.dumps(items)
}
elif HTTP_method == 'GET' and path.startswith('/products/'):
product_id = path_params.get('id')
response = table.get_item(Key={'id': product_id})
item = response.get('Item')
if not item:
return {'statusCode': 404, 'body': JSON.dumps({'error': 'Not found'})}
return {'statusCode': 200, 'body': JSON.dumps(item)}
elif HTTP_method == 'POST' and path == '/products':
body = JSON.loads(event['body'])
table.put_item(Item=body)
print(f"Created product {body.get('id')}")
return {'statusCode': 201, 'body': JSON.dumps(body)}
return {'statusCode': 400, 'body': JSON.dumps({'error': 'Unsupported route'})}
Expected output: The gateway routes GET /products to scan all items, GET /products/{id} to fetch one item, and POST /products to create a new product. API Gateway handles authentication, throttling, and request validation before the Lambda runs.
Rate Limiting at the Gateway Level
API gateways enforce rate limits before requests reach backend services, protecting them from traffic spikes and abuse.
# rate_limit_gateway.py
# Simulated gateway-level Rate Limiting with token bucket
import time
from collections import defaultdict
class TokenBucketRateLimiter:
"""Token bucket rate limiter for API Gateway."""
def __init__(self, capacity, refill_rate):
self.capacity = capacity
self.refill_rate = refill_rate
self.buckets = defaultdict(lambda: {'tokens': capacity, 'last_refill': time.time()})
def allow_request(self, client_id):
"""Check if request is allowed. Returns True/False."""
bucket = self.buckets[client_id]
now = time.time()
elapsed = now - bucket['last_refill']
bucket['tokens'] = min(self.capacity, bucket['tokens'] + elapsed * self.refill_rate)
bucket['last_refill'] = now
if bucket['tokens'] >= 1:
bucket['tokens'] -= 1
return True
return False
limiter = TokenBucketRateLimiter(capacity=10, refill_rate=1)
# Simulate requests
for i in range(15):
client = 'user_123'
allowed = limiter.allow_request(client)
status = 'ALLOWED' if allowed else 'RATE LIMITED'
print(f"Request {i+1}: {status}")
print(f"\nBucket State: {limiter.buckets[client]}")
Expected output:
Request 1: ALLOWED
...
Request 10: ALLOWED
Request 11: RATE LIMITED
...
The first 10 requests are allowed (bucket capacity). Subsequent requests are denied until tokens refill at 1 per second.
Common Errors
1. Exposing Backend Services Directly
When developers bypass the gateway for debugging, they create security holes. Backend services should not be directly accessible — configure security groups to block direct access and force all traffic through the gateway.
2. Not Configuring Proper Timeouts
Gateways have default timeouts (AWS API Gateway: 29 seconds, Kong: 60 seconds). If upstream services take longer, the gateway returns 504. Set appropriate timeouts and use async processing for long-running operations.
3. Overloading the Gateway with Business Logic
The gateway should route and transform, not execute business logic. Putting complex processing in gateway plugins makes the system hard to debug, test, and scale independently.
4. Ignoring Gateway as a Single Point of Failure
A single gateway instance can crash under load. Deploy multiple gateway instances behind a load balancer, enable health checks, and configure circuit breakers for upstream services.
5. Insufficient Monitoring and Logging
Without centralized logging on the gateway, debugging failed requests is extremely difficult. Log request IDs, latencies, status codes, and error details. Use structured JSON logging and ship to a centralized system.
6. Misconfigured CORS Headers
API gateways must handle Cross-Origin Resource Sharing. Misconfigured CORS causes browser errors that are hard to diagnose. Test with curl -H "Origin: https://app.example.com" -v before deploying.
Practice Questions
1. What is the primary purpose of an API Gateway?
An API Gateway serves as a single entry point that handles request routing, authentication, Rate Limiting, and protocol translation, decoupling clients from backend services.
2. How does Kong's plugin architecture work?
Kong uses Lua plugins that hook into the request/response lifecycle. Plugins are configured per service or per route and handle authentication, Rate Limiting, logging, and transformations without modifying backend code.
3. What is the difference between a gateway and a reverse Proxy?
A reverse Proxy only forwards requests. An API Gateway adds cross-cutting features like authentication, Rate Limiting, request transformation, and API Versioning on top of basic proxying.
4. How do you handle CORS with an API Gateway?
Configure the gateway to add CORS headers (Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers) to responses. For preflight OPTIONS requests, return 200 with the appropriate headers without forwarding to upstream.
Challenge
Design an API Gateway configuration for a multi-service platform: Kong gateway routing to three services (users, orders, inventory) with Rate Limiting (100 req/min for anonymous, 1000 req/min for authenticated), JWT authentication, request logging with correlation IDs, and a circuit breaker that drops requests to unhealthy services after 5 failures in 60 seconds.
Mini Project: Local API Gateway with Kong
# Docker-compose.yml for Kong gateway setup
version: '3.8'
services:
kong-database:
image: postgres:16
environment:
POSTGRES_DB: kong
POSTGRES_USER: kong
POSTGRES_PASSWORD: kong
networks:
- kong-net
kong:
image: kong:3.7
depends_on:
- kong-database
environment:
KONG_DATABASE: postgres
KONG_PG_HOST: kong-database
KONG_PG_USER: kong
KONG_PG_PASSWORD: kong
KONG_Proxy_ACCESS_LOG: /dev/stdout
KONG_ADMIN_ACCESS_LOG: /dev/stdout
KONG_Proxy_ERROR_LOG: /dev/stderr
KONG_ADMIN_ERROR_LOG: /dev/stderr
KONG_ADMIN_LISTEN: 0.0.0.0:8001
ports:
- "8000:8000"
- "8443:8443"
- "8001:8001"
- "8444:8444"
networks:
- kong-net
mock-service:
image: kennethreitz/httpbin
networks:
- kong-net
networks:
kong-net:
driver: Bridge
# Deploy and configure
Docker compose up -d
# Wait for Kong to be ready
sleep 10
# Register mock service
curl -s -X POST HTTP://localhost:8001/services \
--data name=mock \
--data URL=HTTP://mock-service:80
# Create route
curl -s -X POST HTTP://localhost:8001/services/mock/routes \
--data paths[]=/mock
# Test the gateway
curl -s HTTP://localhost:8000/mock/get
Expected output: The mock service responds through the gateway. You can verify routing with http://localhost:8000/mock/get returning the httpbin JSON response.
Congratulations on completing this API Gateway tutorial! Next, explore Rate Limiting strategies in depth, then learn about Microservices communication patterns.
- Practice daily — Experiment with Kong plugins for authentication, Caching, and logging
- Build a project — Build a gateway for a three-service architecture with Rate Limiting and monitoring
- Explore related topics — Check out Kong's custom plugin development and AWS API Gateway V2
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro