Cloud allgemein

API-Gateway-Architektur: Patterns für Data-Platforms

Deep-Dive in API-Gateway-Architektur-Patterns für Data-Platforms — Data-Serving-APIs, Rate-Limiting, Auth, Schema-Versioning und das Gateway-as-Data-Mesh-Pattern.

Harbinger Team3. April 20268 Min. LesezeitAktualisiert 14.5.2026
  • api-gateway
  • data-platform
  • data-mesh
  • rest-api
  • rate-limiting
  • platform-engineering
Inhaltsverzeichnis16 Abschnitte

API-Gateway-Architektur: Patterns für Data-Platforms

TL;DR

  • Direkte Warehouse-Connections und Pre-Signed-S3-URLs schaffen Governance-Albträume — wer queryt was, wann und mit welcher Last?
  • API-Gateways geben dir Rate-Limiting pro Consumer, zentrales Audit-Logging, Schema-Abstraktion und sichere API-Versioning.
  • Drei Pattern: Passthrough (simpel, kein Schema-Decoupling), Transformation (empfohlen — stabile API-Contracts), Data-Mesh-Gateway (advanced — Domain-Discovery + Federation).
  • AWS API Gateway + Lambda Authorizer oder Kong sind die Production-Standards. Beide bringen Throttling, JWT-Auth und Audit-Trails.

Data-Platforms haben Daten traditionell über direkte Warehouse-Connections, JDBC-Endpoints und Blob-Storage-Pre-Signed-URLs geliefert. Wenn Organisationen reifen, schaffen diese Ad-hoc-Access-Patterns Governance-Albträume: Wer hat Zugriff worauf, wie viel queryt jeder, und ist der API-Contract stabil genug, dass Downstream-Teams sich darauf verlassen können?

API-Gateways lösen das, indem sie eine managed Control-Plane zwischen Daten-Consumer und Daten-System einfügen. Dieser Guide deckt die Pattern ab, die in Production für Data-Platform-Teams funktionieren.


Warum Data-Platforms API-Gateways brauchen

Der Case für Gateways ist nicht nur Security-Theater. Es geht um operative Capability:

Problem ohne GatewayGateway-Lösung
Direkte Warehouse-Connections hitten Quota-LimitsRate-Limiting pro Consumer
Keine Sichtbarkeit darauf, wer was querytZentralisiertes Access-Logging
Consumer tight gekoppelt an Warehouse-InternalsSchema-Abstraktions-Layer
Auth ad-hoc pro ServiceZentralisiertes OAuth/API-Key-Auth
Kein sicheres Deprecaten alter SchemasAPI-Versioning + Sunset-Headers
Cross-Team-Daten-Access manuell verhandeltSelf-Service-Data-Product-APIs

Gateway-Architektur-Patterns

Pattern 1: Passthrough-Gateway (Simpel)

Das einfachste Pattern: Gateway handhabt Auth und Rate-Limiting, leitet Requests direkt ans Warehouse oder den Data-Service.

flowchart LR
    Client -->|API Key / JWT| GW[API Gateway]
    GW -->|Rate limit check| RL[Rate Limiter
Redis]
    GW -->|Auth verify| Auth[Auth Service
OAuth2]
    GW -->|Proxied query| WH[Data Warehouse
BigQuery / Snowflake]
    WH --> GW --> Client

Gut für: kleine Teams, interne APIs, frühe Data-Products.

Limitation: koppelt API-Schema tight an Warehouse-Schema — jeder Warehouse-Refactor bricht Consumer.

Pattern 2: Transformation-Gateway (Empfohlen)

Das Gateway applies eine Transformation-Schicht: eingehende REST-Requests werden in Warehouse-Queries übersetzt, Responses werden geshapet vor Return.

flowchart LR
    Client -->|REST API| GW[API Gateway]
    GW --> Auth[Auth / Rate Limit]
    GW --> Trans[Transform Layer
OpenAPI → SQL]
    Trans --> WH[Data Warehouse]
    WH --> Trans
    Trans --> Cache[Response Cache
Redis / CDN]
    Cache --> Client

Dieses Pattern enabled stabile API-Contracts, die Warehouse-Refactors überleben. Die Transformation-Schicht owns das Mapping zwischen API-Schema (was Consumer sehen) und Warehouse-Schema (was tatsächlich Daten speichert).

Pattern 3: Data-Mesh-Gateway (Advanced)

In einer Data-Mesh-Architektur ist das Gateway der Entry-Point zu domain-owned Data-Products. Jede Domain exposed ihre Daten über einen standardisierten API-Contract; das Gateway liefert Discovery, Federation und Cross-Domain-Lineage.

flowchart TB
    GW[Central Data Gateway
Discovery + Auth + Lineage]
    
    GW --> D1[Orders Domain API
/v1/orders/*]
    GW --> D2[Customers Domain API
/v1/customers/*]
    GW --> D3[Inventory Domain API
/v1/inventory/*]
    
    D1 --> WH1[Orders Warehouse
BigQuery]
    D2 --> WH2[Customers DB
Postgres read replica]
    D3 --> WH3[Inventory Cache
Redis + S3]

Das zentrale Gateway in einem Data-Mesh-Kontext handhabt:

  • API-Discovery (welche Data-Products existieren und was sie exposen)
  • Cross-Domain-Auth (Consumer authen einmal, Gateway verhandelt Domain-Permissions)
  • Lineage-Tracking (welche Consumer hängen an welchen Data-Products)

Harbinger Explorer passt gut in die Discovery- und Lineage-Schicht dieses Patterns — es pflegt den Cross-Domain-Dependency-Graph, der Data-Mesh-Governance handhabbar macht.


Implementation: AWS API Gateway + Lambda Authorizer

Terraform-Konfiguration

# API Gateway für Data-Platform
resource "aws_api_gateway_rest_api" "data_platform" {
  name        = "data-platform-api"
  description = "Central API gateway for data platform products"

  endpoint_configuration {
    types = ["REGIONAL"]
  }

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect    = "Allow"
      Principal = "*"
      Action    = "execute-api:Invoke"
      Resource  = "arn:aws:execute-api:*:*:*"
      Condition = {
        IpAddress = {
          "aws:SourceIp" = var.allowed_cidr_ranges
        }
      }
    }]
  })
}

resource "aws_api_gateway_deployment" "data_platform" {
  rest_api_id = aws_api_gateway_rest_api.data_platform.id

  triggers = {
    redeployment = sha1(jsonencode(aws_api_gateway_rest_api.data_platform.body))
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_api_gateway_stage" "production" {
  deployment_id = aws_api_gateway_deployment.data_platform.id
  rest_api_id   = aws_api_gateway_rest_api.data_platform.id
  stage_name    = "v1"

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.api_access_log.arn
    format = jsonencode({
      requestId      = "$context.requestId"
      sourceIp       = "$context.identity.sourceIp"
      requestTime    = "$context.requestTime"
      protocol       = "$context.protocol"
      httpMethod     = "$context.httpMethod"
      resourcePath   = "$context.resourcePath"
      routeKey       = "$context.routeKey"
      status         = "$context.status"
      responseLength = "$context.responseLength"
      integrationLatency = "$context.integrationLatency"
      userAgent      = "$context.identity.userAgent"
      # Custom: data platform tracking
      consumerId     = "$context.authorizer.consumerId"
      dataProduct    = "$context.authorizer.dataProduct"
    })
  }

  default_route_settings {
    throttling_burst_limit = 100
    throttling_rate_limit  = 50
  }
}

# Usage plans für Rate-Limiting pro Consumer-Tier
resource "aws_api_gateway_usage_plan" "free_tier" {
  name = "data-platform-free"

  api_stages {
    api_id = aws_api_gateway_rest_api.data_platform.id
    stage  = aws_api_gateway_stage.production.stage_name
  }

  throttle_settings {
    burst_limit = 10
    rate_limit  = 5
  }

  quota_settings {
    limit  = 10000
    period = "MONTH"
  }
}

resource "aws_api_gateway_usage_plan" "professional" {
  name = "data-platform-professional"

  api_stages {
    api_id = aws_api_gateway_rest_api.data_platform.id
    stage  = aws_api_gateway_stage.production.stage_name
  }

  throttle_settings {
    burst_limit = 500
    rate_limit  = 100
  }

  quota_settings {
    limit  = 1000000
    period = "MONTH"
  }
}

Lambda-Authorizer für JWT-Validation

# lambda_authorizer.py
import json
import os
import re
from typing import Optional
import boto3
import jwt
from jwt import PyJWKClient

JWKS_URI = os.environ["JWKS_URI"]  # e.g. https://auth.company.com/.well-known/jwks.json
AUDIENCE = os.environ["TOKEN_AUDIENCE"]

jwks_client = PyJWKClient(JWKS_URI, cache_keys=True)

def handler(event: dict, context) -> dict:
    '''Lambda authorizer: validate JWT and return IAM policy.'''
    token = extract_token(event)
    
    if not token:
        raise Exception("Unauthorized")
    
    try:
        signing_key = jwks_client.get_signing_key_from_jwt(token)
        payload = jwt.decode(
            token,
            signing_key.key,
            algorithms=["RS256"],
            audience=AUDIENCE
        )
    except jwt.ExpiredSignatureError:
        raise Exception("Unauthorized: token expired")
    except jwt.InvalidTokenError as e:
        raise Exception(f"Unauthorized: {e}")
    
    consumer_id = payload.get("sub")
    scopes = payload.get("scope", "").split()
    
    # Map scopes zu API-Gateway-Resource-Permissions
    policy = build_policy(consumer_id, scopes, event["methodArn"])
    policy["context"] = {
        "consumerId": consumer_id,
        "scopes": " ".join(scopes),
        "dataProduct": extract_data_product(event["methodArn"])
    }
    
    return policy


def extract_token(event: dict) -> Optional[str]:
    auth_header = event.get("authorizationToken", "")
    if auth_header.startswith("Bearer "):
        return auth_header[7:]
    return event.get("queryStringParameters", {}).get("token")


def build_policy(principal: str, scopes: list, method_arn: str) -> dict:
    # Parse ARN to determine which resources to allow
    arn_parts = method_arn.split(":")
    region = arn_parts[3]
    account = arn_parts[4]
    api_id = arn_parts[5].split("/")[0]
    stage = arn_parts[5].split("/")[1]
    
    allowed_resources = []
    
    # Map scopes zu erlaubten Resource-Paths
    scope_resource_map = {
        "data:orders:read": f"arn:aws:execute-api:{region}:{account}:{api_id}/{stage}/GET/v1/orders*",
        "data:customers:read": f"arn:aws:execute-api:{region}:{account}:{api_id}/{stage}/GET/v1/customers*",
        "data:inventory:read": f"arn:aws:execute-api:{region}:{account}:{api_id}/{stage}/GET/v1/inventory*",
        "data:admin": f"arn:aws:execute-api:{region}:{account}:{api_id}/{stage}/*/*",
    }
    
    for scope in scopes:
        if scope in scope_resource_map:
            allowed_resources.append(scope_resource_map[scope])
    
    return {
        "principalId": principal,
        "policyDocument": {
            "Version": "2012-10-17",
            "Statement": [{
                "Action": "execute-api:Invoke",
                "Effect": "Allow" if allowed_resources else "Deny",
                "Resource": allowed_resources or [method_arn]
            }]
        }
    }


def extract_data_product(method_arn: str) -> str:
    # Extrahiere Data-Product-Name aus ARN-Path
    parts = method_arn.split("/")
    if len(parts) >= 4:
        return parts[3]  # e.g. "orders" from /v1/orders
    return "unknown"

API-Versioning-Strategie

Data-API-Versioning braucht mehr Gedanken als typische REST-APIs, weil Downstream-Consumer oft Batch-Jobs laufen lassen, die nicht sofort upgedatet werden können.

URL-Based-Versioning (Empfohlen für Data-APIs)

/v1/orders          → stable, supported
/v2/orders          → new schema, active development
/v1/orders [Sunset: 2025-09-01] → deprecated, add Sunset header

Immer Sunset- und Deprecation-Headers für deprecated Versionen inkludieren:

# FastAPI: Data-Product-Endpoint mit Versioning-Headers
from fastapi import FastAPI, Response
from datetime import datetime

app = FastAPI()

@app.get("/v1/orders")
async def get_orders_v1(response: Response):
    # V1 is deprecated — add sunset headers
    response.headers["Deprecation"] = "true"
    response.headers["Sunset"] = "Sat, 01 Sep 2025 00:00:00 GMT"
    response.headers["Link"] = '</v2/orders>; rel="successor-version"'
    
    # Return V1 schema (legacy format)
    return {"orders": [], "total": 0, "page": 1}

@app.get("/v2/orders")
async def get_orders_v2(
    response: Response,
    date_from: str = None,
    date_to: str = None,
    status: str = None,
    limit: int = 100,
    cursor: str = None
):
    # V2: cursor-based pagination, ISO dates, richer filtering
    response.headers["X-Data-Version"] = "2.0"
    
    return {
        "data": [],
        "pagination": {
            "cursor": None,
            "has_more": False,
            "limit": limit
        },
        "meta": {
            "generated_at": datetime.utcnow().isoformat(),
            "data_freshness_seconds": 30
        }
    }

Rate-Limiting-Patterns

Kong-Gateway-Konfiguration

Für Teams mit Kong als Gateway-Schicht:

# Kong declarative config (deck format)
services:
  - name: orders-data-product
    url: http://orders-service.data-platform.svc.cluster.local:8080
    routes:
      - name: orders-api
        paths:
          - /v1/orders
          - /v2/orders
        methods:
          - GET
    plugins:
      - name: rate-limiting
        config:
          minute: 60
          hour: 1000
          policy: redis
          redis_host: redis.infra.svc.cluster.local
          redis_port: 6379
          redis_database: 1
          limit_by: consumer
          
      - name: jwt
        config:
          secret_is_base64: false
          claims_to_verify:
            - exp
            - nbf
            
      - name: request-transformer
        config:
          add:
            headers:
              - "X-Consumer-ID:$(consumer.id)"
              - "X-Data-Platform-Gateway:true"
              
      - name: response-transformer
        config:
          add:
            headers:
              - "X-Rate-Limit-Info:see X-RateLimit-* headers"
              
      - name: http-log
        config:
          http_endpoint: http://audit-log.data-platform.svc.cluster.local/api/access
          method: POST
          content_type: application/json

Observability für Data-APIs

Ein Data-API-Gateway sollte Signale emittieren, die antworten: Wer konsumiert welche Daten, wie schnell, mit welcher Freshness?

Key-Metriken:

  • Request-Rate pro Consumer — Heavy-User identifizieren, bevor sie Quota hitten
  • Latency p95/p99 pro Endpoint — Data-Queries haben lange Tails; Median ist irreführend
  • Cache-Hit-Rate — schlechte Hit-Rates bedeuten teure Warehouse-Queries pro Request
  • Error-Rate by Type — 429s (Quota) vs. 503s (Upstream-Unavailable) brauchen unterschiedliche Responses
  • Daten-Freshness der Responses — kritisch für Near-Real-Time-Consumer

Kombination von Gateway-Metriken mit Data-Platform-Observability (Table-Freshness, Pipeline-Health) in einem Unified-View — wie Harbinger Explorer liefert — gibt Teams das volle Bild von Raw-Ingestion bis API-Consumption.


FAQ

Wann lohnt sich ein API-Gateway vor einer Data-Platform? Sobald mehr als 2-3 Consumer-Teams direkt auf dein Warehouse zugreifen oder du externe Consumer (Customer-APIs, Partner-Feeds) bedienst. Vorher: Direct-Connections mit pgbouncer reichen.

AWS API Gateway oder Kong? AWS API Gateway: zero Ops, tight in AWS-Stack, Lock-in. Kong: provider-agnostisch, self-hosted, mehr Konfig. Für DACH-Compliance mit eu-central-1 ist AWS API Gateway oft das pragmatische Default.

Wie versioniert man Data-APIs sauber? URL-Versioning (/v1/, /v2/) plus Sunset-Headers gibt Downstream-Consumer ein klares Signal. Niemals Breaking-Changes ohne neue Major-Version.

Was kostet ein AWS API Gateway in Production? 3,50 USD pro Million Requests + Caching ab 0,02 USD/Stunde für eine 0,5GB-Cache-Instance. Bei 100M Requests/Monat: ~350 EUR. CloudWatch-Logging und Lambda-Authorizer-Invocations kommen extra.


Summary

API-Gateways für Data-Platforms sind nicht nur ein Security-Checkbox — sie sind das Fundament einer governance-getriebenen, skalierbaren Data-Serving-Schicht. Production-Patterns:

  1. Transformation-Gateways entkoppeln API-Contracts von Warehouse-Internals — immer wert das Investment
  2. Scope-basierte Authorisierung mit JWT ist flexibler als API-Keys für komplexe Permission-Models
  3. URL-Versioning mit Sunset-Headers gibt Downstream-Consumer ein zuverlässiges Deprecation-Signal
  4. Data-Mesh-Gateways, die Domain-APIs föderieren, funktionieren am besten mit Data-Catalog-Backing
  5. Kong oder AWS API Gateway für Rate-Limiting — bau das nicht selbst

Harbinger Explorer 7 Tage kostenlos testen — track API-Consumption-Patterns über deine Data-Products, kriege Sichtbarkeit auf Consumer-Dependencies und manage Data-API-Governance at Scale.

Stand: 14. Mai 2026.

H

Geschrieben von

Harbinger Team

Cloud-, Data- und AI-Engineer in DACH. Schreibt seit 2018 über infrastruktur­kritische Tech-Entscheidungen — keine Marketing- Folien, sondern echte Trade-offs aus Production-Workloads.

Hat dir das geholfen?

Jede Woche ein neuer Artikel über DACH-Cloud, Data und AI — direkt in dein Postfach. Kein Spam, kein Marketing-Sprech.

Kein Spam. 1-Klick-Abmeldung. Datenschutz bei Loops.so.