API Gateway with Kong: Complete Guide for Microservices
Learn how to implement a robust API Gateway with Kong to manage your microservices, including authentication, rate limiting, load balancing, and observability.
This content is free! Help keep the project running.
0737160d-e98f-4a65-8392-5dba70e7ff3eIn the previous article about microservices, we built a user service following Clean Architecture and DDD. Now, let's implement an API Gateway with Kong to manage communication between clients and our microservices.
What is an API Gateway?
An API Gateway is the single entry point for all client requests to your microservices. It acts as a reverse proxy that routes requests, aggregates responses, and provides cross-cutting functionality.
┌─────────────────────────────────────────────────────────────────────┐
│ Clients │
│ (Web, Mobile, IoT, Partners) │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ API Gateway (Kong) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │ Auth │ │ Rate │ │ Load │ │ Logging │ │ Request │ │
│ │ │ │ Limiting│ │Balancing│ │ │ │ Transform │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ User │ │ Order │ │ Product │
│ Service │ │ Service │ │ Service │
└──────────┘ └──────────┘ └──────────┘
Why Kong?
Kong is one of the most popular and robust API Gateways on the market:
Benefits
| Feature | Description |
|---|---|
| High Performance | Built on Nginx/OpenResty, supports millions of requests/second |
| Extensible | 100+ official plugins and support for custom plugins (Lua, Go, JavaScript) |
| Cloud Native | Native Kubernetes integration, official Helm charts |
| Open Source | Free version with all essential features |
| DB-less Mode | Can run without a database, using only declarative configuration |
| Multi-protocol | HTTP, gRPC, WebSocket, TCP/UDP |
Kong vs Alternatives
| Gateway | Strengths | Weaknesses |
|---|---|---|
| Kong | Performance, extensibility, community | Learning curve |
| AWS API Gateway | AWS integration, serverless | Vendor lock-in, cost |
| Traefik | Simple, auto-discovery | Fewer plugins |
| NGINX | Performance | Requires more manual configuration |
| Envoy | Service mesh, observability | Complexity |
Kong Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Kong Gateway │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Kong Proxy │ │
│ │ (Port 8000/8443) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Plugin │→ │ Plugin │→ │ Plugin │→ │ Plugin │→ ... │ │
│ │ │ Auth │ │ Rate │ │ Log │ │Transform│ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Kong Admin API │ │
│ │ (Port 8001/8444) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Data Store │ │
│ │ (PostgreSQL / Cassandra / DB-less) │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Fundamental Concepts
- Services: Represent your upstream microservices
- Routes: Define how requests reach services
- Consumers: Represent users or applications consuming your APIs
- Plugins: Add functionality (auth, rate limiting, logging, etc.)
- Upstreams: Define load balancing and health checks
Project Structure
kong-gateway/
├── docker/
│ ├── docker-compose.yml
│ ├── docker-compose.dev.yml
│ └── docker-compose.prod.yml
│
├── kong/
│ ├── kong.yml # Declarative configuration
│ ├── kong.conf # Kong configuration
│ │
│ ├── plugins/ # Custom plugins
│ │ └── custom-auth/
│ │ ├── handler.lua
│ │ └── schema.lua
│ │
│ └── templates/
│ └── nginx.conf
│
├── config/
│ ├── services/
│ │ ├── user-service.yml
│ │ ├── order-service.yml
│ │ └── product-service.yml
│ │
│ ├── routes/
│ │ ├── user-routes.yml
│ │ ├── order-routes.yml
│ │ └── product-routes.yml
│ │
│ ├── plugins/
│ │ ├── global-plugins.yml
│ │ ├── auth-plugins.yml
│ │ └── rate-limiting.yml
│ │
│ └── consumers/
│ ├── mobile-app.yml
│ ├── web-app.yml
│ └── partners.yml
│
├── scripts/
│ ├── setup.sh
│ ├── deploy.sh
│ ├── backup.sh
│ └── health-check.sh
│
├── k8s/
│ ├── namespace.yaml
│ ├── kong-deployment.yaml
│ ├── kong-service.yaml
│ ├── kong-ingress.yaml
│ ├── configmap.yaml
│ └── secrets.yaml
│
├── monitoring/
│ ├── prometheus/
│ │ └── kong-metrics.yml
│ └── grafana/
│ └── kong-dashboard.json
│
├── tests/
│ ├── integration/
│ │ ├── auth.test.js
│ │ ├── rate-limiting.test.js
│ │ └── routing.test.js
│ └── load/
│ └── k6-load-test.js
│
├── Makefile
└── README.md
Installation and Configuration
Docker Compose (Development)
# docker/docker-compose.ymlversion: '3.8' services: kong-database: image: postgres:15-alpine container_name: kong-database environment: POSTGRES_USER: kong POSTGRES_PASSWORD: kongpass POSTGRES_DB: kong volumes: - kong_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U kong"] interval: 5s timeout: 5s retries: 5 networks: - kong-net kong-migrations: image: kong:3.5-alpine container_name: kong-migrations command: kong migrations bootstrap depends_on: kong-database: condition: service_healthy environment: KONG_DATABASE: postgres KONG_PG_HOST: kong-database KONG_PG_USER: kong KONG_PG_PASSWORD: kongpass KONG_PG_DATABASE: kong networks: - kong-net restart: on-failure kong: image: kong:3.5-alpine container_name: kong depends_on: kong-migrations: condition: service_completed_successfully environment: KONG_DATABASE: postgres KONG_PG_HOST: kong-database KONG_PG_USER: kong KONG_PG_PASSWORD: kongpass KONG_PG_DATABASE: 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, 0.0.0.0:8444 ssl KONG_PROXY_LISTEN: 0.0.0.0:8000, 0.0.0.0:8443 ssl ports: - "8000:8000" - "8443:8443" - "8001:8001" - "8444:8444" healthcheck: test: ["CMD", "kong", "health"] interval: 10s timeout: 10s retries: 10 networks: - kong-net restart: unless-stopped volumes: kong_data: networks: kong-net: driver: bridgeDeclarative Configuration (DB-less)
# kong/kong.yml_format_version: "3.0"_transform: true services: - name: user-service url: http://user-service:3000 connect_timeout: 5000 write_timeout: 60000 read_timeout: 60000 retries: 3 tags: - internal - users - name: order-service url: http://order-service:3001 connect_timeout: 5000 write_timeout: 60000 read_timeout: 60000 retries: 3 - name: product-service url: http://product-service:3002 connect_timeout: 5000 write_timeout: 60000 read_timeout: 60000 retries: 3 routes: - name: user-api service: user-service paths: - /api/v1/users methods: - GET - POST - PUT - DELETE - PATCH strip_path: false preserve_host: true - name: order-api service: order-service paths: - /api/v1/orders methods: - GET - POST - PUT - DELETE - name: product-api service: product-service paths: - /api/v1/products methods: - GET - POST - PUT - DELETE upstreams: - name: user-service-upstream algorithm: round-robin healthchecks: active: healthy: interval: 5 successes: 2 unhealthy: interval: 5 http_failures: 3 http_path: /health/live timeout: 3 targets: - target: user-service-1:3000 weight: 100 - target: user-service-2:3000 weight: 100 consumers: - username: mobile-app custom_id: mobile-app-001 - username: web-app custom_id: web-app-001 - username: partner-api custom_id: partner-001 plugins: - name: correlation-id config: header_name: X-Correlation-ID generator: uuid echo_downstream: true - name: cors config: origins: - "https://app.example.com" methods: - GET - POST - PUT - DELETE - OPTIONS credentials: true max_age: 3600Plugin Implementation
1. JWT Authentication
plugins: - name: jwt route: user-api config: key_claim_name: kid claims_to_verify: - exp header_names: - Authorization maximum_expiration: 86400 - name: key-auth route: partner-api config: key_names: - X-API-Key hide_credentials: true2. Advanced Rate Limiting
plugins: - name: rate-limiting-advanced config: limit: - 1000 window_size: - 60 window_type: sliding strategy: redis redis: host: redis port: 6379 - name: rate-limiting route: user-auth config: minute: 10 hour: 100 policy: redis3. Circuit Breaker Plugin
-- kong/plugins/circuit-breaker/handler.lualocal CircuitBreakerHandler = { VERSION = "1.0.0", PRIORITY = 900,} local CLOSED = "CLOSED"local OPEN = "OPEN"local HALF_OPEN = "HALF_OPEN" local function get_circuit_state(conf, service_id) local cache_key = "circuit:" .. service_id local state = kong.cache:get(cache_key) return state or { status = CLOSED, failures = 0, last_failure = 0 }end function CircuitBreakerHandler:access(conf) local service_id = kong.router.get_service().id local state = get_circuit_state(conf, service_id) if state.status == OPEN then local now = ngx.now() if now - state.last_failure > conf.reset_timeout then state.status = HALF_OPEN else return kong.response.exit(503, { message = "Service temporarily unavailable", circuit_state = OPEN }) end endend return CircuitBreakerHandlerObservability
Prometheus Metrics
plugins: - name: prometheus config: per_consumer: true status_code_metrics: true latency_metrics: true bandwidth_metrics: true upstream_health_metrics: trueAlerts Configuration
groups: - name: kong-alerts rules: - alert: KongHighLatency expr: histogram_quantile(0.95, rate(kong_latency_bucket[5m])) > 1000 for: 5m labels: severity: warning - alert: KongHighErrorRate expr: | sum(rate(kong_http_requests_total{code=~"5.."}[5m])) / sum(rate(kong_http_requests_total[5m])) > 0.05 for: 5m labels: severity: critical - alert: KongUpstreamDown expr: kong_upstream_target_health == 0 for: 1m labels: severity: criticalKubernetes Deployment
Helm Installation
helm repo add kong https://charts.konghq.comhelm repo update helm install kong kong/kong \ --namespace kong \ --create-namespace \ --values values.yamlKong Ingress Controller
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: api-ingress annotations: konghq.com/strip-path: "false" konghq.com/plugins: rate-limiting,jwt-authspec: ingressClassName: kong tls: - hosts: - api.example.com secretName: api-tls rules: - host: api.example.com http: paths: - path: /api/v1/users pathType: Prefix backend: service: name: user-service port: number: 3000Testing
Integration Tests
describe('Kong API Gateway', () => { describe('Routing', () => { it('should route requests to user service', async () => { const response = await axios.get(`${KONG_URL}/api/v1/users`); expect(response.status).toBe(200); }); }); describe('Authentication', () => { it('should reject requests without JWT', async () => { try { await axios.get(`${KONG_URL}/api/v1/users`); } catch (error: any) { expect(error.response.status).toBe(401); } }); }); describe('Rate Limiting', () => { it('should enforce rate limits', async () => { const requests = Array(15).fill(null).map(() => axios.get(`${KONG_URL}/api/v1/auth/login`, { validateStatus: () => true }) ); const responses = await Promise.all(requests); const rateLimited = responses.filter(r => r.status === 429); expect(rateLimited.length).toBeGreaterThan(0); }); });});Load Tests (k6)
import http from 'k6/http';import { check, sleep } from 'k6'; export const options = { stages: [ { duration: '1m', target: 50 }, { duration: '5m', target: 100 }, { duration: '1m', target: 200 }, { duration: '1m', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<500'], errors: ['rate<0.01'], },}; export default function () { const response = http.get(`${BASE_URL}/api/v1/users`, { headers: { 'Authorization': `Bearer ${TOKEN}` } }); check(response, { 'status is 200': (r) => r.status === 200, }); sleep(1);}Production Checklist
Security
- TLS/SSL configured on all ports
- Admin API protected (not publicly exposed)
- Secrets managed via Vault/K8s Secrets
- Rate limiting enabled
- CORS properly configured
Performance
- Connection pooling configured
- Timeouts adjusted per service
- Caching enabled where appropriate
- Gzip compression enabled
Resilience
- Health checks configured
- Circuit breaker implemented
- Retry policies configured
- Load balancing active
Observability
- Prometheus metrics exposed
- Structured logs
- Distributed tracing enabled
- Alerts configured
Conclusion
A well-implemented API Gateway is fundamental for a robust microservices architecture. Kong provides a solid foundation with:
- Performance: Millions of requests per second
- Flexibility: 100+ plugins and customization support
- Observability: Integrated metrics, logs, and tracing
- Security: Authentication, authorization, and rate limiting
- Resilience: Circuit breaker, retry, and health checks
In the next article, learn how to implement Messaging with RabbitMQ for asynchronous communication between microservices.
Enjoyed the content? Your contribution helps keep everything online and free!
0737160d-e98f-4a65-8392-5dba70e7ff3e