Skip to content

Kong API Gateway — Complete Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you'll learn about Kong API Gateway. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Kong is an open-source API gateway built on NGINX, using a plugin architecture for authentication, Rate Limiting, logging, and request/response transformation.

What You'll Learn

By the end of this lesson, you will configure Kong services and routes, enable plugins for auth and rate limiting, and understand the Kong plugin ecosystem.

Why It Matters

Kong is one of the most popular open-source gateways, used by companies like Yelp, WeWork, and Grab. Its plugin architecture makes it extensible without modifying backend code.

Real-World Use

A microservice platform registers each service in Kong, adds JWT authentication and rate limiting via plugins, and uses Kong's admin API for dynamic configuration.

Kong Architecture

flowchart LR
    Client -->|Port 8000| Kong[Kong Gateway]
    Kong -->|Routes| S1[Service 1]
    Kong -->|Routes| S2[Service 2]
    Admin[Admin API - Port 8001] --> Kong
    Kong -->|Plugins| Plugin[Plugin: Auth/Rate Limit/Log]
    style Kong fill:#22c55e,color:#fff

Kong Service and Route Configuration

# kong_config.py
import json
from typing import Dict, List, Optional

class KongConfigurator:
    def __init__(self, admin_url: str = "http://localhost:8001"):
        self.admin_url = admin_url
        self.services: Dict[str, dict] = {}
        self.routes: Dict[str, list] = {}
        self.plugins: Dict[str, list] = {}

    def add_service(self, name: str, url: str, protocol: str = "http",
                    retries: int = 5, connect_timeout: int = 60000):
        self.services[name] = {
            "name": name,
            "url": url,
            "protocol": protocol,
            "retries": retries,
            "connect_timeout": connect_timeout,
            "write_timeout": 60000,
            "read_timeout": 60000,
        }
        self.routes[name] = []
        self.plugins[name] = []

    def add_route(self, service: str, paths: List[str],
                  methods: Optional[List[str]] = None,
                  hosts: Optional[List[str]] = None,
                  strip_path: bool = True):
        if service not in self.services:
            return
        self.routes[service].append({
            "service": service,
            "paths": paths,
            "methods": methods,
            "hosts": hosts,
            "strip_path": strip_path,
        })

    def add_plugin(self, service: str, plugin_name: str, config: dict):
        if service not in self.plugins:
            return
        self.plugins[service].append({
            "name": plugin_name,
            "service": service,
            "config": config,
        })

    def export(self) -> dict:
        return {
            "services": list(self.services.values()),
            "routes": {s: self.routes[s] for s in self.services},
            "plugins": {s: self.plugins[s] for s in self.services},
        }

kong = KongConfigurator()
kong.add_service("users", "http://user-service:3000")
kong.add_service("orders", "http://order-service:4000")
kong.add_service("products", "http://product-service:5000")

kong.add_route("users", ["/api/users", "/api/users/:id"],
               methods=["GET", "POST", "PUT", "DELETE"])
kong.add_route("orders", ["/api/orders"],
               methods=["GET", "POST"])
kong.add_route("products", ["/api/products"],
               methods=["GET"], strip_path=False)

kong.add_plugin("users", "rate-limiting", {
    "minute": 100,
    "policy": "local",
})
kong.add_plugin("users", "jwt", {})
kong.add_plugin("orders", "rate-limiting", {
    "minute": 50,
    "policy": "local",
})

config = kong.export()
print(f"Services: {len(config['services'])}")
print(f"Routes: {sum(len(r) for r in config['routes'].values())}")
print(f"Plugins: {sum(len(p) for p in config['plugins'].values())}")

for svc in config['services']:
    print(f"\n  Service: {svc['name']} -> {svc['url']}")
    for route in config['routes'][svc['name']]:
        print(f"    Route: {route['paths']}")
    for plugin in config['plugins'][svc['name']]:
        print(f"    Plugin: {plugin['name']}")

Expected output:

Services: 3
Routes: 3
Plugins: 3

  Service: users -> http://user-service:3000
    Route: ['/api/users', '/api/users/:id']
    Plugin: rate-limiting
    Plugin: jwt

  Service: orders -> http://order-service:4000
    Route: ['/api/orders']
    Plugin: rate-limiting

  Service: products -> http://product-service:5000
    Route: ['/api/products']

Kong Authentication Plugin

# kong_auth.py
import time
from typing import Dict, List, Optional

class KongAuthPlugin:
    def __init__(self, plugin_name: str = "key-auth"):
        self.plugin_name = plugin_name
        self.consumers: Dict[str, dict] = {}
        self.credentials: Dict[str, str] = {}

    def add_consumer(self, consumer_id: str, username: str,
                     custom_id: Optional[str] = None):
        self.consumers[consumer_id] = {
            "username": username,
            "custom_id": custom_id or consumer_id,
            "created_at": time.time(),
        }

    def add_key_auth(self, consumer_id: str, key: str):
        self.credentials[key] = consumer_id

    def authenticate(self, headers: Dict) -> Optional[dict]:
        api_key = headers.get("apikey") or headers.get("X-Api-Key")
        if not api_key:
            return None

        consumer_id = self.credentials.get(api_key)
        if not consumer_id:
            return None

        consumer = self.consumers.get(consumer_id)
        if not consumer:
            return None

        return {
            "authenticated": True,
            "consumer": consumer["username"],
            "consumer_id": consumer_id,
        }

auth = KongAuthPlugin()
auth.add_consumer("c_1", "acme-corp")
auth.add_consumer("c_2", "startup-inc")
auth.add_key_auth("c_1", "key_acme_123")
auth.add_key_auth("c_2", "key_startup_456")

tests = [
    {"apikey": "key_acme_123"},
    {"X-Api-Key": "key_startup_456"},
    {"X-Api-Key": "invalid_key"},
    {},
]
for headers in tests:
    result = auth.authenticate(headers)
    if result:
        print(f"Authed: consumer={result['consumer']}, id={result['consumer_id']}")
    else:
        print("Auth failed")

Expected output:

Authed: consumer=acme-corp, id=c_1
Authed: consumer=startup-inc, id=c_2
Auth failed
Auth failed

Common Mistakes

1. Using in-memory Database in Production

Kong's default in-memory DB (DB-less) is for development. Use PostgreSQL or Cassandra for production.

2. Not Enabling Health Checks

Without health checks, Kong routes to unhealthy upstream services. Configure active or passive health checks.

3. Overloading with Too Many Plugins

Each plugin adds latency. Disable unused plugins and prefer built-in plugins over custom Lua plugins.

4. Forgetting to Strip Path Prefix

Setting strip_path=false when the backend expects root paths causes routing errors. Configure per-route.

5. No Admin API Security

The admin API (port 8001) should not be exposed publicly. Use firewall rules or authentication.

Practice Questions

1. How does Kong's plugin architecture work?

Plugins hook into the request/response lifecycle. They are configured per service, route, or globally and can modify any aspect of the request.

2. What database options does Kong support?

PostgreSQL (production) and in-memory DB-less mode (dev/test). Cassandra support was deprecated.

3. How do you add authentication to a Kong service?

Enable the JWT, Key-Auth, OAuth2, or Basic-Auth plugin on the service, then configure credentials for consumers.

4. What is a Kong consumer?

A consumer represents a user or application that consumes the API. Consumers have credentials associated with them for authentication.

Challenge

Configure Kong with three services (users, orders, payments), add JWT authentication, rate limiting (100 req/min for users, 50 for orders, 20 for payments), and request logging plugin.

FAQ

Is Kong free to use?

Kong Gateway (OSS) is free. Kong Enterprise adds features like dev portal, analytics, and RBAC.

Can Kong handle WebSockets?

Yes. Kong supports WebSocket connections with stream routes and the TCP/UDP capabilities.

How does Kong perform under load?

Kong handles 10K+ req/s per instance with minimal latency overhead (1-5ms) in production.

Can I write custom Kong plugins?

Yes. Kong plugins are written in Lua. The PDK (Plugin Development Kit) provides APIs for request/response manipulation.

Does Kong support service mesh?

Kong Mesh is a separate product for service mesh. Kong Gateway handles north-south traffic.

Mini Project: Kong Configuration Generator

# kong_generator.py
import json
from typing import Dict, List, Optional

class KongConfigGenerator:
    def __init__(self):
        self.config = {"services": [], "routes": [], "plugins": []}

    def add_service(self, name: str, url: str, retries: int = 5):
        svc = {
            "name": name, "url": url, "protocol": "http",
            "retries": retries, "connect_timeout": 60000,
            "write_timeout": 60000, "read_timeout": 60000,
        }
        self.config["services"].append(svc)
        return svc

    def add_route(self, service: str, paths: List[str],
                  methods: Optional[List[str]] = None,
                  strip_path: bool = True):
        route = {
            "service": {"name": service},
            "paths": paths,
            "strip_path": strip_path,
        }
        if methods:
            route["methods"] = methods
        self.config["routes"].append(route)

    def add_plugin(self, service: str, name: str, config: dict):
        self.config["plugins"].append({
            "name": name,
            "service": {"name": service},
            "config": config,
        })

    def generate(self) -> str:
        return json.dumps(self.config, indent=2)

gen = KongConfigGenerator()
gen.add_service("users", "http://users:3000")
gen.add_service("orders", "http://orders:4000")
gen.add_route("users", ["/api/users"], methods=["GET", "POST"])
gen.add_route("orders", ["/api/orders"], methods=["GET"])
gen.add_plugin("users", "rate-limiting", {"minute": 100})
gen.add_plugin("orders", "jwt", {})

config = json.loads(gen.generate())
print(f"Services: {len(config['services'])}")
print(f"Routes: {len(config['routes'])}")
print(f"Plugins: {len(config['plugins'])}")
for s in config['services']:
    print(f"  {s['name']} -> {s['url']}")

Expected output:

Services: 2
Routes: 2
Plugins: 2
  users -> http://users:3000
  orders -> http://orders:4000

What's Next

You understand Kong. Next, learn about NGINX as API gateway, then explore Envoy proxy.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro