Skip to contentPedro Farbo

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.

PIX:0737160d-e98f-4a65-8392-5dba70e7ff3e

In 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

FeatureDescription
High PerformanceBuilt on Nginx/OpenResty, supports millions of requests/second
Extensible100+ official plugins and support for custom plugins (Lua, Go, JavaScript)
Cloud NativeNative Kubernetes integration, official Helm charts
Open SourceFree version with all essential features
DB-less ModeCan run without a database, using only declarative configuration
Multi-protocolHTTP, gRPC, WebSocket, TCP/UDP

Kong vs Alternatives

GatewayStrengthsWeaknesses
KongPerformance, extensibility, communityLearning curve
AWS API GatewayAWS integration, serverlessVendor lock-in, cost
TraefikSimple, auto-discoveryFewer plugins
NGINXPerformanceRequires more manual configuration
EnvoyService mesh, observabilityComplexity

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)

yaml
# 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: bridge

Declarative Configuration (DB-less)

yaml
# 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: 3600

Plugin Implementation

1. JWT Authentication

yaml
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: true

2. Advanced Rate Limiting

yaml
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: redis

3. Circuit Breaker Plugin

lua
-- 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 CircuitBreakerHandler

Observability

Prometheus Metrics

yaml
plugins:  - name: prometheus    config:      per_consumer: true      status_code_metrics: true      latency_metrics: true      bandwidth_metrics: true      upstream_health_metrics: true

Alerts Configuration

yaml
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: critical

Kubernetes Deployment

Helm Installation

bash
helm repo add kong https://charts.konghq.comhelm repo update helm install kong kong/kong \  --namespace kong \  --create-namespace \  --values values.yaml

Kong Ingress Controller

yaml
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: 3000

Testing

Integration Tests

typescript
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)

javascript
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:

  1. Performance: Millions of requests per second
  2. Flexibility: 100+ plugins and customization support
  3. Observability: Integrated metrics, logs, and tracing
  4. Security: Authentication, authorization, and rate limiting
  5. Resilience: Circuit breaker, retry, and health checks

In the next article, learn how to implement Messaging with RabbitMQ for asynchronous communication between microservices.

PF
About the author

Pedro Farbo

Platform Engineering Lead & Solutions Architect with 10+ years of experience. CEO at Farbo TSC. Expert in Microservices, Kong, Backstage, and Cloud.

Enjoyed the content? Your contribution helps keep everything online and free!

PIX:0737160d-e98f-4a65-8392-5dba70e7ff3e