DEV Community

Cover image for Managing a scalable microservices API Gateway using KrakenD
Gosu Code
Gosu Code

Posted on • Originally published at Medium

Managing a scalable microservices API Gateway using KrakenD

The fastest API Gateway in the market, ready to run on premises, cloud, or hybrid.

The gateway sits in front of your microservices to provide access control, security, throttling, analytics, and much more. KrakenD is not only a proxy but a Backend for Frontend that can aggregate and transform multiple calls simultaneously, avoiding network traffic and reducing bandwidth consumer to the end-user.

KrakenD can be set up in a single krakend.json file. A single, massive krakend.json with hardcoded backends is an anti-pattern. Pros treat gateway configurations like clean, modular code.

1. The Directory Structure

Instead of one file, we’re splitting gateway configuration by domain, environments, and general settings which keeps it dynamic and organized as well.

├── .env
├── .gitignore
├── docker-compose.yml
└── config/
    ├── krakend.tmpl
    ├── settings/
    │   ├── dev
    |   |   ├── hosts.json
    │   └── prod
    |       └── hosts.json
    └── endpoints/
        ├── products.tmpl
        └── users.tmpl
Enter fullscreen mode Exit fullscreen mode

2. Step-by-Step Implementation

Step 1: Container Orchestration (docker-compose.yml)

Instruct KrakenD to enable Flexible Configuration (FC_ENABLE), declare the directory where your modular endpoint templates live (FC_TEMPLATES), and point to your environment variables (FC_SETTINGS).

services:
  gateway:
    container_name: api-gateway
    image: krakend:2.13.8
    ports:
      - "3000:3000"
    volumes:
      - ./config:/etc/krakend:ro
    environment:
      - FC_ENABLE=1
      - FC_TEMPLATES=/etc/krakend/endpoints
      - FC_SETTINGS=/etc/krakend/settings/dev
    command: ["run", "-c", "/etc/krakend/krakend.tmpl"]
    restart: unless-stopped
Enter fullscreen mode Exit fullscreen mode

FC_ENABLE=1: Tells KrakenD to turn on the template engine.
FC_TEMPLATES: Points to the directory containing your modular template files (like your endpoints/ folder).
FC_SETTINGS: Points to the file or directory containing your environment variables (like hosts.json).

Step 2: Environment Settings (config/settings/dev/hosts.json)

Use this file to store microservice host URLs and environment-specific strings, keeping them out of your main routing logic.

{
  "hosts": {
    "product_service": "http://product-api:5001",
    "user_service": "http://user-api:5002"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Modular Endpoints (config/endpoints/users.tmpl)

Define template configurations using standard Go templating.

{
  "endpoint": "/api/auth/register",
  "method": "POST",
  "backend": [{
    "url_pattern": "/api/auth/register",
    "host": ["{{ .hosts.user_service }}"]
  }]
},
{
  "endpoint": "/api/auth/login",
  "method": "POST",
  "backend": [{
    "url_pattern": "/api/auth/login",
    "host": ["{{ .hosts.user_service }}"]
  }]
}
Enter fullscreen mode Exit fullscreen mode

Notice how we reference "{{ .hosts.user_service }}"? At build time, KrakenD compiles this template and automatically swaps that placeholder with the URL from your environment settings file (hosts.json).

Q. what is this .tmpl file?

A: .tmpl (Template) file is a plain text blueprint containing static configuration mixed with dynamic placeholders (like {{ .hosts.user_service }}).
It leverages Go's templating engine to act as a flexible skeleton before the final configuration is compiled.

Example:

1. Template (users.tmpl):

"host": [ "{{ .hosts.user_service }}" ]
Enter fullscreen mode Exit fullscreen mode

2. Variables (hosts.json):

{ 
  "product_service": "http://localhost:5001",
  "user_service": "http://localhost:5002"
}
Enter fullscreen mode Exit fullscreen mode

3. What KrakenD actually runs in memory:

"host": [ "http://localhost:5002" ]
Enter fullscreen mode Exit fullscreen mode

Q. "host": [ "{{ .hosts.user_service }}" ] how is it getting the user service url with that?

A: It works via KrakenD's Flexible Configuration(FC) engine during container startup. It executes in two quick steps:

  1. The Flag: In your docker-compose.yml, you set FC_SETTINGS=/etc/krakend/settings/dev. This forces KrakenD to load your hosts.json file into memory as the core data dictionary.

  2. The Evaluation: As KrakenD compiles the configuration, it encounters the template syntax {{ .hosts.user_service }}. The dot (.) represents the root of your loaded hosts.json file. It navigates down the keys: hostsuser_service, extracts the string "http://user-api:5002", and replaces the placeholder entirely before compiling the route.

Step 4: The Entrypoint (config/krakend.tmpl)

Tie everything together into the main skeleton. KrakenD will pull the files from your FC_TEMPLATES (defined in docker compose file) directory and compile them here.

{
  "version": 3,
  "port": 8080,
  "timeout": "3s",
  "endpoints": [
    {{ template "products.tmpl" . }},
    {{ template "users.tmpl" . }}
  ]
}
Enter fullscreen mode Exit fullscreen mode

Now let's go a little bit further: extra_config

extra_config helps to enable and configure plugins, middleware, and extended features. It unlocks advanced capabilities such as security, rate limiting, logging and metrics.
krakend.tmpl

{
  "version": 3,
  "port": 8080,
  "timeout": "3s",
  "endpoints": [
    {{ template "products.tmpl" . }},
    {{ template "users.tmpl" . }}
  ],
  "extra_config": {
    "telemetry/logging": {
      "level": "INFO",
      "prefix": "[KRAKEND]",
      "stdout": true
    },
    "security/cors": {
      "allow_origins": ["*"],
      "allow_methods": ["GET", "POST", "PUT", "PATCH", "DELETE"],
      "allow_headers": ["Authorization", "Content-Type"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In krakend.tmpl we are using it for logging and security(to handle cors).

user.tmpl

{
  "endpoint": "/api/auth/register",
  "method": "POST",
  "extra_config": {
    "qos/ratelimit/router": { "max_rate": 200, "capacity": 200, "every": "1s" }
  },
  "backend": [{
    "url_pattern": "/api/auth/register",
    "host": ["{{ .hosts.user_service }}"],
    "extra_config": {
      "qos/circuit-breaker": { "max_errors": 10, "interval": 60, "timeout": 30, "name": "cb-users-post-register", "log_status_change": true }
    }
  }]
},
{
  "endpoint": "/api/auth/login",
  "method": "POST",
  "extra_config": {
    "qos/ratelimit/router": { "max_rate": 200, "capacity": 200, "every": "1s" }
  },
  "backend": [{
    "url_pattern": "/api/auth/login",
    "host": ["{{ .hosts.user_service }}"],
    "extra_config": {
      "qos/circuit-breaker": { "max_errors": 10, "interval": 60, "timeout": 30, "name": "cb-users-post-login", "log_status_change": true }
    }
  }]
}
Enter fullscreen mode Exit fullscreen mode

Here, we are using it for rate limiting and circuit-breaker.
rate limiting: 200 request per sec
circuit-breaker: Oh boy! it's on whole another level. Let's talk in detail below.

Circuit Breaker

It's job is to STOP KrakenD from sending requests to a failing service, giving that service time to recover and preventing a total system crash.

"extra_config": {
      "qos/circuit-breaker": { 
        "max_errors": 10, 
        "interval": 60, 
        "timeout": 30, 
        "name": "cb-users-post-login", 
        "log_status_change": true 
      }
}
Enter fullscreen mode Exit fullscreen mode

How above specific setting works

Closed: Closed is a healthy state, it means circuit breaker is close and requests can pass normally.
Open: If KrakenD counts max_error(10 errors) within time interval(60s), it blocks all the requests to the backend server.
Half Open: After the 30-second timeout expires, the circuit breaker enters the Half-Open state. In that state, it allows to pass limited number of requests to test if the backend is actually stable. If the test passes, Circuit Breaker goes in Close state else goes in Open state.
Some unexplained configs in the circuit breaker
name: A friendly name to follow this circuit breaker's activity in the logs.
log_status_change: Whether to log the changes of state of this circuit breaker or not. Default to false.

More features provided by KrakenD

1. Data Manipulation & Aggregation: Merge multiple microservice responses into a single JSON object.
2. Authentication Offloading: Native JWT validation plugins allow the gateway to reject unauthorized requests at the edge before they even touch your internal networks.
3. Response Caching: In-memory or Redis caching options.
4. Comprehensive Metrics: Native integration with Prometheus, Jaeger, OpenTelemetry, and ELK stack for plug-and-play system visibility.

As I implement more features, I'll update this setup. How are you currently managing your API Gateway configurations? Let me know in the comments!

Have a nice day!

Top comments (0)