API Gateway

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/v1 saat routing ke microservice agar service tidak perlu aware prefix gateway.
  • API versioning mudah dengan routing berbasis path (/v1/, /v2/) — tambahkan Deprecation dan Sunset headers untuk memberi tahu client tentang versi yang akan dihentikan.
  • Circuit breaker sederhana bisa dilakukan dengan fail_duration dan max_fails pada 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.

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact