API Gateway Selection Criteria

Picking the wrong gateway forces a painful migration once traffic scales or compliance requirements shift. This page provides a decision framework grounded in API Gateway Fundamentals & Architecture — covering control-plane design, plugin extensibility, routing capabilities, protocol translation support, and the operational trade-offs between Kong 3.x, Envoy 1.32+, Tyk 5.x, and NGINX. By the end you will have a repeatable evaluation method and concrete config examples for each gateway, not a vendor marketing comparison.


Architectural Baseline

Before evaluating any product, establish a clear model of the two planes every gateway contains:

  • Control plane — the process (or cluster) that reads declarative configuration and distributes it to data-plane workers. Its job is to reconcile desired state with running state.
  • Data plane — the hot path that proxies requests. It must remain available even when the control plane is unreachable, which means it must hold a local copy of the last-known good configuration.

The gap between these planes creates the most common selection mistake: teams benchmark raw request throughput without testing what happens during a configuration change. If the control plane broadcasts a full-state replacement to all workers simultaneously, every worker pauses to re-parse the config, causing latency spikes under load. Gateways that use atomic diff-based propagation — xDS incremental APIs, or Kong’s declarative deck sync — avoid this problem.

Two additional architectural dimensions shape every downstream decision:

Deployment topology — Where does the gateway sit? Edge-only, per-cluster ingress, or per-pod sidecar? Envoy is the only gateway that can cleanly serve all three roles without modification. Kong and Tyk are optimised for centralised ingress; NGINX can run at the edge but lacks dynamic routing without a commercial control plane.

Plugin execution model — Does the gateway run plugins in-process (Lua, Go native), in a subprocess (NGINX Perl/Python), or in a WebAssembly sandbox (Envoy Wasm, Kong 3.x Wasm)? In-process plugins have the lowest latency overhead but can crash the proxy on a bug; Wasm sandboxes add roughly 0.3–0.8 ms per plugin invocation but isolate faults. Settle this question before reaching the middleware chain evaluation layer, because it constrains which gateways are viable.


Primary Concept: Control-Plane Architecture Deep-Dive

Kong 3.x — Database-backed vs DB-less

Kong ships two control-plane modes. In database mode the control plane writes to PostgreSQL and workers poll for changes every db_update_frequency seconds (default: 5 s). In DB-less mode a single kong.yml declarative config is loaded at startup and reloaded on SIGHUP — no database required.

# Kong 3.x — DB-less declarative config (kong.yml)
_format_version: "3.0"
_transform: true

services:
  - name: payments-api
    url: http://payments-svc:8080
    connect_timeout: 3000
    write_timeout: 5000
    read_timeout: 5000
    routes:
      - name: payments-v2
        paths:
          - /api/v2/payments
        methods:
          - POST
          - GET
        strip_path: false
    plugins:
      - name: rate-limiting
        config:
          minute: 1000
          policy: redis
          redis_host: redis-cluster.internal
          redis_port: 6379

In production, DB-less mode is preferable for immutable infrastructure: the config is version-controlled, diff-reviewable, and applied with kong reload (zero connection drops). Database mode suits teams that need the Kong Admin API for dynamic changes at runtime.

Config propagation timing: DB-less reload completes in roughly 200 ms on a 10 k-route config; database mode depends on db_update_frequency plus PostgreSQL read latency. If your release pipeline needs sub-second propagation across 20 or more worker nodes, DB-less with a config management pipeline (deck + CI) wins.

Envoy 1.32+ — xDS Dynamic Control Plane

Envoy does not have a built-in control plane; it consumes configuration from an external xDS management server via gRPC. The xDS suite covers Listener Discovery Service (LDS), Route Discovery Service (RDS), Cluster Discovery Service (CDS), and Endpoint Discovery Service (EDS). Incremental xDS (delta xDS) sends only the changed resources, so a single route update does not re-transmit the entire routing table.

# Envoy 1.32 — static bootstrap with xDS management server reference
node:
  id: edge-proxy-01
  cluster: edge-ingress

dynamic_resources:
  lds_config:
    resource_api_version: V3
    api_config_source:
      api_type: GRPC
      transport_api_version: V3
      grpc_services:
        - envoy_grpc:
            cluster_name: xds-management-server
      set_node_on_first_message_in_delta_stream: true

  cds_config:
    resource_api_version: V3
    api_config_source:
      api_type: GRPC
      transport_api_version: V3
      grpc_services:
        - envoy_grpc:
            cluster_name: xds-management-server

static_resources:
  clusters:
    - name: xds-management-server
      type: STRICT_DNS
      connect_timeout: 1s
      load_assignment:
        cluster_name: xds-management-server
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: control-plane.internal
                      port_value: 18000
      http2_protocol_options: {}

The Envoy control plane (Istio Pilot, Contour, or a custom Go server using go-control-plane) can push updates in under 100 ms. If the xDS server becomes unreachable, Envoy continues serving traffic with the last-received configuration — a critical safety property for production.

Decision Flow: Choosing Your Gateway

The diagram below captures the primary branching logic. Work through it top-to-bottom; the first “Yes” branch that matches your requirements determines the starting recommendation.

API Gateway Selection Decision Flow A flowchart guiding engineers through gateway selection: starting from deployment topology need (sidecar / service mesh), then plugin ecosystem priority, then native Go developer portal requirement, arriving at Kong, Envoy, Tyk, or NGINX as the recommended gateway. Start: New gateway needed Define your deployment topology Need sidecar or service mesh role? Yes Envoy 1.32+ xDS + sidecar-ready No Plugin ecosystem a top priority? Yes Kong 3.x Lua + Wasm plugins No Need native Go or developer portal? Yes Tyk 5.x Go plugins + portal No NGINX / NJS Static + scripted edge

Secondary Concept: Plugin Extensibility and Edge Cases

Lua plugins in Kong (in-process, ~0 ms overhead)

-- Kong 3.x custom plugin: enforce X-Api-Version header
local plugin = {
  PRIORITY = 1000,
  VERSION  = "1.0.0",
}

function plugin:access(conf)
  local version = kong.request.get_header("X-Api-Version")
  if not version then
    return kong.response.exit(400, {
      message = "X-Api-Version header is required",
      code    = "MISSING_API_VERSION",
    })
  end

  -- pass version downstream as a sanitised header
  kong.service.request.set_header("X-Upstream-Api-Version", version)
end

return plugin

Lua plugins execute in the same Nginx worker process, so a nil-pointer bug can crash that worker. Kong 3.x introduced Wasm-based plugin execution as an alternative. Enable it with wasm: on in kong.conf and distribute the plugin as a compiled .wasm module — the sandbox prevents crashes from escaping the plugin boundary.

Envoy Wasm filter (isolated, ~0.5 ms overhead)

# Envoy 1.32 — Wasm HTTP filter
http_filters:
  - name: envoy.filters.http.wasm
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
      config:
        name: "api-version-enforcer"
        root_id: "api_version_root"
        vm_config:
          vm_id: "api_version_vm"
          runtime: "envoy.wasm.runtime.v8"
          code:
            local:
              filename: "/etc/envoy/filters/api_version.wasm"
          allow_precompiled: true
        configuration:
          "@type": "type.googleapis.com/google.protobuf.StringValue"
          value: '{"required_header": "X-Api-Version"}'

Wasm filters can be hot-reloaded without restarting Envoy by updating the xDS configuration to point to a new .wasm binary. The V8 sandbox isolates faults but adds roughly 0.3–0.8 ms overhead per filter invocation at p99 under contention.

Tyk 5.x — Go plugin middleware

Tyk supports compiled Go plugins loaded as shared objects (.so files). This gives native Go performance with full access to the Go standard library, but plugins must be compiled against the exact Tyk version’s Go toolchain.

// Tyk 5.x API definition — custom Go middleware
{
  "api_id": "payments-api",
  "name": "Payments API",
  "version_data": {
    "not_versioned": false,
    "versions": {
      "v2": {
        "name": "v2",
        "use_extended_paths": true,
        "extended_paths": {
          "go_plugins": [
            {
              "path": "/opt/tyk-gateway/middleware/api_version_enforcer.so",
              "func_name": "EnforceApiVersion",
              "method": "",
              "pattern": ".*"
            }
          ]
        }
      }
    }
  }
}

The major operational risk with Tyk Go plugins is binary compatibility: upgrading Tyk requires recompiling all plugins. Teams that update frequently should maintain a plugin build pipeline keyed to the Tyk version tag.


Comparative Implementation Table

Gateway Control-plane model Plugin model Protocol support Key trade-off
Kong 3.x DB-less declarative or PostgreSQL-backed Admin API Lua (in-process) or Wasm sandbox HTTP/1.1, HTTP/2, gRPC, WebSocket, TCP Richest plugin hub; Lua crashes escape the sandbox unless Wasm mode is enabled
Envoy 1.32+ External xDS server (delta xDS, sub-100 ms propagation) Wasm (V8 sandbox) or native C++ filters HTTP/1.1, HTTP/2, HTTP/3, gRPC, WebSocket, raw TCP Steepest learning curve; requires running or buying an xDS control plane
Tyk 5.x Tyk Dashboard (Redis-backed) or DB-less MDCB Compiled Go .so plugins HTTP/1.1, HTTP/2, gRPC, GraphQL, TCP Go plugins must be rebuilt per Tyk version; built-in developer portal is a differentiator
NGINX (OSS + NJS) Static config files + nginx -s reload NJS (JavaScript) modules HTTP/1.1, HTTP/2, WebSocket, raw TCP No dynamic routing without NGINX Plus or a third-party config manager; highest raw throughput

Operational Gotchas

1. Config propagation races in Kong database mode

When multiple Kong nodes share a PostgreSQL control plane and db_update_frequency is set to the default 5 s, nodes can hold divergent route tables for up to 10 s after a change is committed. Under A/B canary deployments this can route the same user to different route versions within the same session.

Remediation: Either switch to DB-less mode with declarative config and kong reload, or reduce db_update_frequency to 1 s and monitor PostgreSQL read latency — values below 50 ms are safe.

2. Envoy xDS connection loss

If the xDS management server becomes unreachable, Envoy holds the last good configuration indefinitely. This is the correct safe default. The failure mode engineers miss is startup without an xDS server — Envoy will not serve traffic until it receives at least one LDS response. Set initial_fetch_timeout and configure static fallback listeners for health-check endpoints.

# Envoy 1.32 — xDS fetch timeout + static health-check listener
dynamic_resources:
  lds_config:
    initial_fetch_timeout: 10s   # wait 10 s for LDS before failing
    resource_api_version: V3
    api_config_source: {}        # abbreviated — see bootstrap above

static_resources:
  listeners:
    - name: health-check-static
      address:
        socket_address: { address: 0.0.0.0, port_value: 8888 }
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: health
                route_config:
                  virtual_hosts:
                    - name: local
                      domains: ["*"]
                      routes:
                        - match: { prefix: "/healthz" }
                          direct_response:
                            status: 200
                            body:
                              inline_string: "ok"
                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

3. Tyk plugin binary drift

A Tyk 5.x upgrade that bumps the internal Go version will silently fail to load plugins compiled against the previous toolchain. The gateway logs plugin could not be loaded and falls back to passing requests without that middleware — a silent security regression if the plugin enforced authentication and token validation.

Remediation: Pin the Tyk version in your docker-compose.yml or Helm values, gate Tyk upgrades on a plugin build step in CI, and add a startup health check that verifies each .so is loadable before the process accepts traffic.

4. NGINX rate-limiting state not shared across workers

NGINX OSS limit_req_zone stores counters in a per-worker shared memory zone (zone=api_rate:10m). Under high concurrency each worker has a slightly different view of the counter, meaning the actual request rate allowed is approximately limit × worker_count. This makes rate limiting and throttling inaccurate for strict per-user quotas.

# NGINX — shared memory rate-limit zone (OSS: per-instance, not cluster-wide)
http {
  limit_req_zone $http_x_api_key zone=per_key:10m rate=100r/s;

  server {
    location /api/ {
      limit_req zone=per_key burst=20 nodelay;
      limit_req_status 429;
      proxy_pass http://upstream_svc;
    }
  }
}

For cluster-wide enforcement use NGINX Plus with a shared zone sync peer, or route to Kong or Tyk where Redis-backed rate limiting is first-class.

5. Missing strip_path on Kong routes causes double-path upstream calls

A common mistake when migrating from a reverse proxy is forgetting strip_path: true on Kong routes with a prefix match. The upstream service receives /api/v2/payments/api/v2/payments/123 because Kong appends the full request path to the upstream URL.

Signal: HTTP 404 from upstream with no matching route in the upstream logs. Fix: Set strip_path: true (the Kong default) or use path_handling: v1 for explicit path rewriting.


Production Configuration Checklist

  • Control-plane propagation tested under load: push a config change while sustaining 5 k req/s and verify zero 502s
  • Plugin timeout budgets set per stage: timeout_budget_ms (Kong) or per-filter timeout in the Envoy HttpConnectionManager
  • Wasm sandbox enabled for any third-party plugins (Kong: wasm: on; Envoy: runtime: envoy.wasm.runtime.v8)
  • DB-less config committed to version control and applied through CI (deck sync for Kong, tyk-sync for Tyk)
  • initial_fetch_timeout set on all Envoy xDS configs with static health-check listeners as fallback
  • Tyk plugin build pipeline gates gateway upgrades on successful .so compilation and load test
  • NGINX rate-limit zones replaced with Redis-backed counters if per-user enforcement is required
  • strip_path behaviour verified for every Kong route by replaying sample requests against staging
  • Zero-trust mTLS configured between gateway and upstream services, not just at the public edge
  • Distributed tracing headers (traceparent, tracestate) injected by the gateway and validated end-to-end through at least two upstream hops

FAQ

What is the most important factor when choosing an API gateway?

Control-plane architecture is typically the deciding factor. A gateway whose control plane cannot propagate configuration changes atomically will cause request drops during rollouts. Evaluate whether the gateway uses eventual-consistency-safe diff algorithms or full-state replacements, and whether the data plane can continue serving traffic independently if the control plane becomes temporarily unavailable.

When should I choose Envoy over Kong?

Choose Envoy when you need xDS-native dynamic configuration, deep gRPC and HTTP/2 support with precise flow-control, or sidecar-less service-mesh integration with Istio. Choose Kong when your team needs a rich, stable plugin ecosystem with a familiar Lua or Go SDK, a GUI admin API, and a gentler operational learning curve.

Can I run multiple API gateways in the same platform?

Yes, and it is common at scale. A typical pattern is Envoy or NGINX at the internet edge for raw TLS termination and DDoS absorption, Kong or Tyk as the API management layer for policy enforcement, and a service mesh sidecar for east-west traffic between microservices. Each layer handles its own concerns without duplicating policy.


Up: API Gateway Fundamentals & Architecture