Inhaltsverzeichnis16 Abschnitte
- TL;DR
- Warum Data-Platforms API-Gateways brauchen
- Gateway-Architektur-Patterns
- Pattern 1: Passthrough-Gateway (Simpel)
- Pattern 2: Transformation-Gateway (Empfohlen)
- Pattern 3: Data-Mesh-Gateway (Advanced)
- Implementation: AWS API Gateway + Lambda Authorizer
- Terraform-Konfiguration
- Lambda-Authorizer für JWT-Validation
- API-Versioning-Strategie
- URL-Based-Versioning (Empfohlen für Data-APIs)
- Rate-Limiting-Patterns
- Kong-Gateway-Konfiguration
- Observability für Data-APIs
- FAQ
- Summary
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 Gateway | Gateway-Lösung |
|---|---|
| Direkte Warehouse-Connections hitten Quota-Limits | Rate-Limiting pro Consumer |
| Keine Sichtbarkeit darauf, wer was queryt | Zentralisiertes Access-Logging |
| Consumer tight gekoppelt an Warehouse-Internals | Schema-Abstraktions-Layer |
| Auth ad-hoc pro Service | Zentralisiertes OAuth/API-Key-Auth |
| Kein sicheres Deprecaten alter Schemas | API-Versioning + Sunset-Headers |
| Cross-Team-Daten-Access manuell verhandelt | Self-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:
- Transformation-Gateways entkoppeln API-Contracts von Warehouse-Internals — immer wert das Investment
- Scope-basierte Authorisierung mit JWT ist flexibler als API-Keys für komplexe Permission-Models
- URL-Versioning mit Sunset-Headers gibt Downstream-Consumer ein zuverlässiges Deprecation-Signal
- Data-Mesh-Gateways, die Domain-APIs föderieren, funktionieren am besten mit Data-Catalog-Backing
- 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.
Geschrieben von
Harbinger Team
Cloud-, Data- und AI-Engineer in DACH. Schreibt seit 2018 über infrastrukturkritische 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.