API Gateway #
API Gateway adalah single entry point untuk semua request ke microservices. Daripada setiap service memiliki port dan domain sendiri yang di-expose ke internet, semua request masuk melalui satu gateway yang menangani routing, autentikasi, rate limiting, logging, dan transformasi request/response secara terpusat.
Caddy adalah pilihan yang sangat baik sebagai API gateway ringan berkat kemampuan routing, header manipulation, dan reverse proxy-nya yang powerful.
Arsitektur API Gateway #
┌─────────────────────────────┐
Internet ──────► │ Caddy API Gateway │
│ - Routing │
│ - Auth (JWT/API Key) │
│ - Rate Limiting │
│ - CORS │
│ - Logging │
└──────────────┬──────────────┘
│
┌──────────┬──────────┼──────────┬──────────┐
▼ ▼ ▼ ▼ ▼
User Svc Product Svc Order Svc Auth Svc Search Svc
:3001 :3002 :3003 :3004 :3005
Routing ke Multiple Microservices #
api.example.com {
# ── Global Middleware ─────────────────────────────────────────
log {
output file /var/log/caddy/api-gateway.log
format json
}
encode gzip zstd
# CORS terpusat
@options method OPTIONS
handle @options {
header Access-Control-Allow-Origin "https://app.example.com"
header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, OPTIONS"
header Access-Control-Allow-Headers "Content-Type, Authorization, X-API-Key"
header Access-Control-Max-Age "86400"
respond "" 204
}
header Access-Control-Allow-Origin "https://app.example.com"
# Security headers
header {
-Server
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
}
# ── Service Routing ───────────────────────────────────────────
handle /api/v1/users* {
uri strip_prefix /api/v1
reverse_proxy user-service:3001 {
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up X-Gateway-Version "1.0"
}
}
handle /api/v1/products* {
uri strip_prefix /api/v1
reverse_proxy product-service:3002
}
handle /api/v1/orders* {
uri strip_prefix /api/v1
reverse_proxy order-service:3003
}
handle /api/v1/search* {
uri strip_prefix /api/v1
reverse_proxy search-service:3005 {
# Search butuh lebih lama
transport http {
response_header_timeout 30s
}
}
}
# Default: 404
handle {
respond "Not Found" 404
}
}
API Versioning #
api.example.com {
# v1 routes → service v1
handle /api/v1/* {
uri strip_prefix /api/v1
reverse_proxy user-service-v1:3001
}
# v2 routes → service v2 (baru, masih ditest)
handle /api/v2/* {
uri strip_prefix /api/v2
reverse_proxy user-service-v2:3011
}
# Latest → alias ke v2
handle /api/latest/* {
uri path_regexp ^/api/latest/ /api/v2/
reverse_proxy user-service-v2:3011
}
# Deprecated v1 warning header
handle /api/v1/* {
header Deprecation "true"
header Sunset "Sat, 01 Jan 2025 00:00:00 GMT"
header Link "</api/v2/>; rel=\"successor-version\""
}
}
Autentikasi Terpusat di Gateway #
api.example.com {
# Endpoint publik (tidak perlu auth)
handle /api/v1/auth/login {
reverse_proxy auth-service:3004
}
handle /api/v1/auth/register {
reverse_proxy auth-service:3004
}
handle /api/public/* {
uri strip_prefix /api/public
reverse_proxy public-service:3006
}
# Semua endpoint lain butuh autentikasi
# Opsi 1: Basic Auth (sederhana)
basicauth {
apiuser $2a$14$hashHere
}
# Opsi 2: JWT via plugin (lebih proper untuk API)
# Memerlukan plugin caddy-jwt atau caddy-security
# Teruskan user ID ke backend setelah auth
handle {
reverse_proxy backend:3000 {
header_up X-User-ID {http.auth.user.id}
header_up X-Auth-Method "gateway"
}
}
}
Rate Limiting per Service #
api.example.com {
# Rate limit berbeda untuk service berbeda
@expensive_ops path /api/v1/reports* /api/v1/export*
rate_limit @expensive_ops {
zone expensive {
key {remote_host}
window 1h
events 10 # Hanya 10 report per jam
}
}
@regular_api path /api/v1/*
rate_limit @regular_api {
zone api_general {
key {remote_host}
window 1m
events 100
}
}
# Routing setelah rate limit checks
handle /api/v1/reports* {
reverse_proxy report-service:3007
}
handle /api/v1/* {
reverse_proxy main-backend:3000
}
}
Request dan Response Transformation #
api.example.com {
# Tambah headers yang diperlukan semua backend
handle /api/* {
reverse_proxy backend:3000 {
# Tambah ke request menuju backend
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up X-Request-ID {http.request.uuid}
header_up X-Gateway "caddy"
# Hapus dari request sebelum ke backend
header_up -X-Powered-By
header_up -Server
# Hapus dari response sebelum ke client
header_down -X-Internal-Service
header_down -X-Debug-Info
# Tambah ke response menuju client
header_down X-Gateway-Response-Time "{duration}"
}
}
}
Circuit Breaker Pattern #
api.example.com {
handle /api/v1/payment* {
reverse_proxy payment-service:3008 payment-service-backup:3018 {
# Passive health check — circuit breaker sederhana
fail_duration 30s # Anggap down selama 30 detik setelah gagal
max_fails 3 # Anggap down setelah 3 kali gagal
# Active health check
health_uri /health
health_interval 10s
health_timeout 5s
# Fallback ke backup service
lb_policy first # Coba primary dulu, baru backup
}
}
}
Monitoring dan Observability #
# Dashboard monitoring API gateway sederhana
#!/bin/bash
echo "=== API Gateway Status ==="
echo ""
echo "--- Upstream Health ---"
curl -s http://localhost:2019/reverse_proxy/upstreams/ | \
jq -r '.[] | "\(.address): \(if .healthy then "✓ UP" else "✗ DOWN" end)"'
echo ""
echo "--- Request Statistics (last 5 min) ---"
cat /var/log/caddy/api-gateway.log | \
jq 'select(.ts > (now - 300))' | \
jq -s '
{
total: length,
errors: [.[] | select(.status >= 400)] | length,
slow: [.[] | select(.duration > 1)] | length,
avg_duration: ([.[] | .duration] | add / length * 1000 | round)
}
'
echo ""
echo "--- Top Endpoints ---"
cat /var/log/caddy/api-gateway.log | \
jq -r '.request.uri | split("?")[0]' | \
sort | uniq -c | sort -rn | head -5
Ringkasan #
- Caddy API gateway cocok untuk small to medium microservices — untuk skala besar dengan kebutuhan service discovery dinamis, pertimbangkan Kong atau Envoy.
- Letakkan semua cross-cutting concerns (auth, CORS, rate limiting, logging) di gateway sehingga setiap microservice tidak perlu mengimplementasikannya sendiri.
- Gunakan
uri strip_prefix /api/v1saat routing ke microservice agar service tidak perlu aware prefix gateway.- API versioning mudah dengan routing berbasis path (
/v1/,/v2/) — tambahkanDeprecationdanSunsetheaders untuk memberi tahu client tentang versi yang akan dihentikan.- Circuit breaker sederhana bisa dilakukan dengan
fail_durationdanmax_failspada reverse proxy — Caddy akan otomatis bypass backend yang bermasalah.- Tambahkan
X-Request-ID: {http.request.uuid}ke setiap request untuk korelasi log antar semua microservice.
← Sebelumnya: SPA React Berikutnya: Error Umum →
Service Discovery Sederhana dengan File Konfigurasi #
#!/bin/bash
# update-gateway.sh — Update konfigurasi gateway dari service registry
# Baca daftar service dari file registry
# /etc/caddy/services.json
# {
# "user-service": "user-service:3001",
# "product-service": "product-service:3002"
# }
# Generate Caddyfile dari template dan registry
python3 << 'EOF'
import json
with open('/etc/caddy/services.json') as f:
services = json.load(f)
routes = ""
for name, addr in services.items():
path = name.replace('-service', '').replace('-', '/')
routes += f"""
handle /api/v1/{path}/* {{
uri strip_prefix /api/v1/{path}
reverse_proxy {addr}
}}
"""
print(f"""api.example.com {{
encode gzip zstd
{routes}
handle {{
respond "Not Found" 404
}}
}}""")
EOF
Pendekatan ini — meski sederhana — memungkinkan manajemen service yang lebih dinamis tanpa perlu edit Caddyfile secara manual setiap kali service berubah.