Chapitre 12 — API Gateway#
Un API Gateway est l’infrastructure qui se positionne entre les clients externes et l’ensemble des services internes d’une plateforme. Il constitue le point d’entrée unique par lequel tout le trafic entrant transite, quelle que soit la destination finale. Ce chapitre examine la structure de ce composant, ses responsabilités, les patterns d’implémentation courants et les solutions disponibles sur le marché.
Rôle d’un API Gateway#
Point d’entrée unique#
Dans une architecture microservices, l’absence de point d’entrée centralisé oblige chaque client à connaître l’adresse de chaque service. Un client mobile doit contacter le service users, puis le service products, puis le service orders — avec des adresses, des ports, des protocoles et des mécanismes d’authentification potentiellement différents pour chacun. Ce couplage fort est une source de fragilité.
L’API Gateway fournit une façade unifiée. Côté client, il n’existe qu’un seul hostname (api.exemple.com). L’adresse, le schéma d’authentification et le format des requêtes sont stables même si l’architecture interne évolue.
Cross-cutting concerns#
Les préoccupations transversales (cross-cutting concerns) sont les fonctionnalités que tous les services partagent, mais qu’il serait inefficace et dangereux de réimplémenter dans chaque service individuellement :
Authentification et autorisation (validation du token)
Rate limiting et quotas
Logging des accès
Collecte de métriques
Propagation du contexte de tracing distribué
TLS termination
Compression des réponses
Ces responsabilités sont centralisées au niveau gateway. Les services backend reçoivent des requêtes déjà authentifiées, enrichies d’un contexte d’identité, et peuvent se concentrer sur leur logique métier.
Découplage clients/services#
Le gateway absorbe les différences entre ce que les clients attendent et ce que les services exposent. Si un service change son API interne, le gateway peut continuer à exposer l’ancienne interface en transformant les requêtes à la volée. Si un service est remplacé par une nouvelle implémentation, les clients n’en savent rien.
Note
Le gateway n’est pas un ESB (Enterprise Service Bus). Il ne contient pas de logique métier. Sa responsabilité est technique : routing, sécurité, observabilité, transformation de format. La logique métier reste dans les services.
Routing#
Path-based routing#
La règle la plus courante : le préfixe du chemin de l’URL détermine le service cible.
/api/users/* → service users (port 8001)
/api/products/* → service products (port 8002)
/api/orders/* → service orders (port 8003)
Le gateway reçoit GET /api/users/42, extrait le préfixe /api/users, et transmet la requête au service users avec le chemin reécrit en /42 ou maintenu selon la configuration.
Host-based routing#
Le header Host (ou X-Forwarded-Host) détermine la destination. Utile pour les architectures multi-tenant où chaque tenant a son sous-domaine :
tenant-a.api.exemple.com → cluster A
tenant-b.api.exemple.com → cluster B
Header-based routing#
Des headers spécifiques peuvent guider le routing. X-API-Version: v2 peut router vers un service v2, X-Beta-Tester: true vers un cluster de staging. Ce pattern est utilisé pour les canary releases contrôlées (voir chapitre 15).
Load balancing#
Le gateway distribue le trafic entre les instances d’un même service. Les algorithmes courants :
Round-robin : distribution cyclique équitable
Weighted round-robin : pondération par capacité de l’instance
Least connections : instance ayant le moins de connexions actives
Consistent hashing : même client → même instance (session affinity)
Versioning au niveau gateway#
Le gateway peut gérer le versioning d’API sans que les services n’en soient conscients. Accept: application/vnd.exemple.v2+json ou /v2/users peut être routé vers une version spécifique du service, ou le gateway peut transformer le format v1 en format attendu par le service v2.
Authentification centralisée#
Validation JWT au gateway#
Le JWT est validé une seule fois, au gateway. Si la signature est invalide ou le token expiré, la requête est rejetée avec un 401 Unauthorized sans atteindre les services backend. Cette centralisation évite que chaque service implémente la cryptographie de validation.
# Exemple statique : middleware de validation JWT dans un gateway FastAPI simplifié
import jwt
from fastapi import Request, HTTPException
JWKS_URL = "https://auth.exemple.com/.well-known/jwks.json"
async def jwt_validation_middleware(request: Request, call_next):
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Token manquant")
token = auth_header.removeprefix("Bearer ")
try:
payload = jwt.decode(
token,
key=get_public_key_from_jwks(token),
algorithms=["RS256"],
audience="api.exemple.com",
)
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expiré")
except jwt.InvalidTokenError as e:
raise HTTPException(status_code=401, detail=f"Token invalide : {e}")
# Transmission de l'identité aux services backend
request.state.user_id = payload["sub"]
request.state.scopes = payload.get("scope", "").split()
response = await call_next(request)
return response
Transmission d’identité : X-User-ID header#
Après validation, le gateway injecte l’identité dans la requête transmise aux services backend via des headers custom :
X-User-ID: usr_42
X-User-Scopes: read:products write:orders
X-Tenant-ID: tenant_abc
Les services backend font confiance à ces headers (communication interne sur réseau privé) et n’ont pas à valider le token JWT eux-mêmes.
OAuth token introspection#
Pour les tokens opaques (non-JWT), le gateway contacte l’endpoint POST /oauth/introspect du serveur d’autorisation pour valider le token et récupérer les claims. Ce mécanisme est plus coûteux (appel réseau synchrone sur chaque requête) et nécessite généralement un cache de résultats d’introspection avec un TTL court.
Important
Les headers X-User-ID et similaires ne doivent jamais être acceptés des clients externes. Le gateway doit les supprimer des requêtes entrantes avant de les propager, pour éviter qu’un client malveillant usurpe une identité.
Rate limiting au gateway#
Stratégies de rate limiting#
Le rate limiting peut opérer à plusieurs granularités :
Par IP : limite les abus anonymes, protège contre les DDoS basiques
Par user : limite équitable par compte authentifié
Par API key : adapté aux intégrations partenaires
Par plan commercial : free/pro/enterprise avec quotas différents
Plans tarifaires et quotas#
# Exemple statique : configuration de plans dans un gateway
PLANS = {
"free": {"requests_per_minute": 60, "requests_per_day": 1_000},
"pro": {"requests_per_minute": 600, "requests_per_day": 50_000},
"enterprise": {"requests_per_minute": 6000, "requests_per_day": 1_000_000},
}
Réponse 429 avec Retry-After#
Lorsqu’un client dépasse son quota, le gateway retourne 429 Too Many Requests avec les headers informatifs :
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711234567
Content-Type: application/json
{"error": "rate_limit_exceeded", "retry_after": 30}
Le header Retry-After indique le nombre de secondes avant de réessayer, permettant au client d’implémenter un backoff correct.
Transformation de requêtes/réponses#
Header injection et stripping#
Le gateway peut ajouter des headers aux requêtes sortantes (X-Request-ID, X-Forwarded-For, identité utilisateur) et supprimer des headers des réponses entrantes (headers internes révélant l’infrastructure, comme X-Powered-By: service-v2.3.1).
Body transformation#
Dans certains cas, le gateway transforme le corps de la requête ou de la réponse : renommage de champs pour maintenir la compatibilité avec une ancienne version d’API, filtrage de champs sensibles, ajout de métadonnées de pagination.
Protocol translation : REST vers gRPC#
Un pattern avancé : les clients externes communiquent en REST/JSON (universel, simple), mais les services internes utilisent gRPC (efficace, typé). Le gateway transcrit automatiquement :
Client → GET /api/users/42 (HTTP/1.1 JSON)
Gateway → UserService.GetUser({id: "42"}) (gRPC/protobuf)
Gateway → 200 OK {"id": "42", "name": "Alice"} (HTTP/1.1 JSON)
Cette translation est supportée nativement par Envoy et peut être configurée dans Kong via des plugins.
Circuit breaking et résilience#
Timeout au gateway#
Chaque route peut avoir un timeout configuré. Si le service backend ne répond pas dans le délai imparti, le gateway retourne 504 Gateway Timeout plutôt que de maintenir la connexion ouverte indéfiniment.
# Exemple statique : configuration de timeout par route dans Traefik
http:
routers:
users-router:
rule: "PathPrefix(`/api/users`)"
service: users-service
middlewares:
- users-timeout
middlewares:
users-timeout:
forwardAuth:
responseHeaderTimeout: "5s"
Retry#
Pour les erreurs transitoires (502, 503, 504), le gateway peut réessayer automatiquement vers une autre instance du service. Les requêtes non-idempotentes (POST, PATCH) ne doivent pas être retentées sans précaution.
Circuit breaker au niveau gateway#
Le circuit breaker surveille le taux d’erreur d’un service sur une fenêtre glissante. Si ce taux dépasse un seuil, le circuit s’ouvre et le gateway arrête de transmettre les requêtes au service, retournant immédiatement une réponse de fallback. Après un délai, quelques requêtes de test sont laissées passer (état half-open) pour tester le rétablissement du service.
Attention
Le circuit breaker au gateway protège le service surchargé d’une avalanche de requêtes, mais il ne remplace pas la résilience dans les services eux-mêmes. Un circuit breaker en aval dans le service reste nécessaire pour les appels inter-services internes.
Observabilité#
Access logs#
Le gateway est le point de collecte naturel des logs d’accès : chaque requête, son origine, sa destination, son statut, sa latence. Ces logs sont la source primaire d’audit et de débogage.
[2026-03-25T10:42:00Z] GET /api/users/42 200 45ms user=usr_42 route=users-service
[2026-03-25T10:42:01Z] POST /api/orders 201 123ms user=usr_42 route=orders-service
[2026-03-25T10:42:03Z] GET /api/products 429 2ms user=usr_99 route=ratelimited
Métriques par route#
Le gateway expose des métriques Prometheus agrégées par route : taux de requêtes, distribution de latence (p50, p95, p99), taux d’erreur. Ces métriques permettent d’identifier les services dégradés et de dimensionner les ressources.
Distributed tracing et propagation du trace context#
Le gateway initie le span racine du trace distribué (W3C Trace Context, traceparent header) et le propage aux services backend. Chaque service crée un span enfant, et le système de tracing (Jaeger, Tempo) reconstitue l’arbre complet des appels pour une requête donnée.
Client → Gateway (crée traceparent: 00-abc123-0000-01)
→ Service users (span enfant, traceparent inchangé)
→ Service cache Redis (span enfant)
Solutions#
Kong#
Kong est un gateway open-source basé sur Nginx/OpenResty, extensible par des plugins Lua. Sa configuration peut être déclarative (fichier YAML) ou dynamique via une API admin.
# Exemple statique : configuration Kong déclarative (deck)
_format_version: "3.0"
services:
- name: users-service
url: http://users-svc:8001
routes:
- name: users-route
paths:
- /api/users
strip_path: true
- name: products-service
url: http://products-svc:8002
routes:
- name: products-route
paths:
- /api/products
strip_path: true
plugins:
- name: jwt
service: users-service
config:
claims_to_verify:
- exp
- name: rate-limiting
config:
minute: 60
policy: redis
redis_host: redis
redis_port: 6379
Traefik#
Traefik est un reverse proxy et gateway orienté cloud-native, dont la configuration peut être extraite automatiquement des labels Docker ou des annotations Kubernetes.
# Exemple statique : configuration Traefik avec middlewares
http:
routers:
api-router:
rule: "Host(`api.exemple.com`)"
middlewares:
- auth-middleware
- rate-limit-middleware
- add-headers
service: backend-service
middlewares:
auth-middleware:
forwardAuth:
address: "http://auth-service:9000/validate"
authResponseHeaders:
- "X-User-ID"
- "X-User-Scopes"
rate-limit-middleware:
rateLimit:
average: 100
burst: 50
period: "1m"
add-headers:
headers:
customRequestHeaders:
X-Gateway: "traefik"
customResponseHeaders:
X-Powered-By: "" # suppression du header interne
services:
backend-service:
loadBalancer:
servers:
- url: "http://backend-1:8000"
- url: "http://backend-2:8000"
AWS API Gateway#
Solution managée d’Amazon, intégrée nativement à l’écosystème AWS (Lambda, Cognito, IAM, CloudWatch). Deux modes : REST API (fonctionnalités complètes, facturation par requête) et HTTP API (plus léger et moins cher). Avantage majeur : zéro infrastructure à gérer. Inconvénient : vendor lock-in fort et coûts qui s’envolent à grande échelle.
Nginx comme gateway simplifié#
Nginx peut jouer le rôle d’un gateway léger pour les architectures moins complexes, via proxy_pass, limit_req_zone pour le rate limiting et auth_request pour la validation d’authentification déléguée.
# Exemple statique : Nginx comme gateway simplifié
upstream users_backend {
server users-svc:8001;
server users-svc-2:8001;
}
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
server {
listen 443 ssl http2;
server_name api.exemple.com;
location /api/users/ {
auth_request /validate_token;
auth_request_set $user_id $upstream_http_x_user_id;
limit_req zone=api burst=20 nodelay;
proxy_pass http://users_backend/;
proxy_set_header X-User-ID $user_id;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_read_timeout 5s;
}
location = /validate_token {
internal;
proxy_pass http://auth-svc:9000/validate;
}
}
API Gateway vs Service Mesh#
Un service mesh (Istio, Linkerd) est un réseau de proxies sidecar qui gèrent la communication entre services à l’intérieur du cluster. Un API Gateway gère la communication entre les clients externes et le cluster (trafic nord-sud). La frontière est nette :
Dimension |
API Gateway |
Service Mesh |
|---|---|---|
Trafic |
Nord-sud (externe → interne) |
Est-ouest (service → service) |
Position |
Périmètre du cluster |
Intérieur du cluster |
Authentification |
Token client (JWT/OAuth) |
mTLS inter-services |
Audience |
Équipe API / Sécurité |
Équipe plateforme / SRE |
Les deux coexistent dans les architectures matures : le gateway filtre et authentifie le trafic entrant, le mesh sécurise et observ le trafic interne. Certains projets (Envoy Gateway, Kong Mesh) visent à unifier les deux niveaux avec le même plan de contrôle.
Note
La confusion vient des solutions qui couvrent les deux cas (Istio peut aussi gérer le trafic nord-sud via son Ingress Gateway). Dans tous les cas, les responsabilités restent distinctes même si l’outil est le même.
Simulations et visualisations#
Simulation de routing au gateway#
import re
from dataclasses import dataclass, field
from typing import Callable, Optional
@dataclass
class Route:
path_prefix: str
service: str
strip_prefix: bool = True
@dataclass
class Request:
method: str
path: str
headers: dict = field(default_factory=dict)
@dataclass
class Response:
status: int
body: str
headers: dict = field(default_factory=dict)
class SimpleGateway:
def __init__(self):
self.routes: list[Route] = []
self.middlewares: list[Callable] = []
def add_route(self, route: Route):
self.routes.append(route)
def use(self, middleware: Callable):
self.middlewares.append(middleware)
def _find_route(self, path: str) -> Optional[Route]:
# Tri par préfixe le plus long (priorité au plus spécifique)
candidates = [r for r in self.routes if path.startswith(r.path_prefix)]
if not candidates:
return None
return max(candidates, key=lambda r: len(r.path_prefix))
def handle(self, request: Request) -> Response:
# Application des middlewares dans l'ordre
for mw in self.middlewares:
result = mw(request)
if result is not None:
return result # middleware a court-circuité
route = self._find_route(request.path)
if route is None:
return Response(404, '{"error": "route_not_found"}')
# Réécriture du chemin
forwarded_path = request.path
if route.strip_prefix:
forwarded_path = request.path[len(route.path_prefix):]
if not forwarded_path.startswith("/"):
forwarded_path = "/" + forwarded_path
return Response(
200,
f'{{"forwarded_to": "{route.service}", "path": "{forwarded_path}"}}',
headers={"X-Gateway-Route": route.service}
)
# Construction du gateway
gw = SimpleGateway()
# Middleware : suppression du header X-User-ID entrant (sécurité)
def strip_internal_headers(req: Request) -> Optional[Response]:
req.headers.pop("X-User-ID", None)
req.headers.pop("X-User-Scopes", None)
return None # continuer
# Middleware : injection de l'ID de requête
def inject_request_id(req: Request) -> Optional[Response]:
import uuid
req.headers["X-Request-ID"] = str(uuid.uuid4())[:8]
return None
gw.use(strip_internal_headers)
gw.use(inject_request_id)
# Routes
gw.add_route(Route("/api/users", "users-service"))
gw.add_route(Route("/api/users/admin", "users-admin-service")) # plus spécifique
gw.add_route(Route("/api/products", "products-service"))
gw.add_route(Route("/api/orders", "orders-service"))
# Tests de routing
test_cases = [
Request("GET", "/api/users/42"),
Request("GET", "/api/users/admin/stats"), # route plus spécifique
Request("POST", "/api/orders"),
Request("GET", "/api/unknown/endpoint"),
Request("GET", "/api/users/99", headers={"X-User-ID": "attaquant"}),
]
print(f"{'Méthode':<8} {'Chemin':<35} {'Status':<8} {'Corps'}")
print("-" * 80)
for req in test_cases:
resp = gw.handle(req)
print(f"{req.method:<8} {req.path:<35} {resp.status:<8} {resp.body}")
if "X-User-ID" not in req.headers:
if req.path.endswith("attaquant") or "attaquant" in str(req.headers):
pass # déjà stripped
Méthode Chemin Status Corps
--------------------------------------------------------------------------------
GET /api/users/42 200 {"forwarded_to": "users-service", "path": "/42"}
GET /api/users/admin/stats 200 {"forwarded_to": "users-admin-service", "path": "/stats"}
POST /api/orders 200 {"forwarded_to": "orders-service", "path": "/"}
GET /api/unknown/endpoint 404 {"error": "route_not_found"}
GET /api/users/99 200 {"forwarded_to": "users-service", "path": "/99"}
Simulation de rate limiting par plan avec token bucket#
import time
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from collections import defaultdict
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
class TokenBucket:
"""Token bucket algorithm pour le rate limiting."""
def __init__(self, rate: float, capacity: float):
self.rate = rate # tokens ajoutés par seconde
self.capacity = capacity # capacité maximale du bucket
self.tokens = capacity
self.last_refill = time.monotonic()
def consume(self, tokens: float = 1.0) -> bool:
now = time.monotonic()
elapsed = now - self.last_refill
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
self.last_refill = now
if self.tokens >= tokens:
self.tokens -= tokens
return True # requête acceptée
return False # rate limit dépassé
# Plans commerciaux : (rate req/s, burst capacity)
PLANS = {
"free": TokenBucket(rate=1.0, capacity=10),
"pro": TokenBucket(rate=10.0, capacity=100),
"enterprise": TokenBucket(rate=100.0, capacity=500),
}
# Simulation : 200 requêtes en rafale pour chaque plan
n_requests = 200
results = {}
for plan_name, bucket in PLANS.items():
accepted = 0
rejected = 0
# Réinitialisation pour simulation propre
bucket.tokens = bucket.capacity
bucket.last_refill = time.monotonic()
timeline = []
for i in range(n_requests):
ok = bucket.consume()
if ok:
accepted += 1
else:
rejected += 1
timeline.append(accepted / (i + 1) * 100)
results[plan_name] = {
"accepted": accepted,
"rejected": rejected,
"acceptance_rate": accepted / n_requests * 100,
"timeline": timeline,
}
fig, axes = plt.subplots(1, 2, figsize=(13, 5))
# Graphique 1 : taux d'acceptation par plan
plans = list(results.keys())
accepted_counts = [results[p]["accepted"] for p in plans]
rejected_counts = [results[p]["rejected"] for p in plans]
x = np.arange(len(plans))
width = 0.35
colors = sns.color_palette("muted", 3)
bars1 = axes[0].bar(x - width/2, accepted_counts, width, label="Acceptées", color=colors[0])
bars2 = axes[0].bar(x + width/2, rejected_counts, width, label="Rejetées (429)", color=colors[2])
axes[0].set_xlabel("Plan commercial")
axes[0].set_ylabel("Nombre de requêtes")
axes[0].set_title("Rate limiting par plan — 200 requêtes en rafale")
axes[0].set_xticks(x)
axes[0].set_xticklabels(["Free\n(1 req/s)", "Pro\n(10 req/s)", "Enterprise\n(100 req/s)"])
axes[0].legend()
for bar in bars1:
axes[0].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
str(int(bar.get_height())), ha="center", va="bottom", fontsize=9)
for bar in bars2:
axes[0].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
str(int(bar.get_height())), ha="center", va="bottom", fontsize=9)
# Graphique 2 : évolution du taux d'acceptation cumulé
for plan_name, data in results.items():
axes[1].plot(range(1, n_requests + 1), data["timeline"],
label=plan_name.capitalize(), linewidth=2)
axes[1].set_xlabel("Numéro de la requête")
axes[1].set_ylabel("Taux d'acceptation cumulé (%)")
axes[1].set_title("Évolution du taux d'acceptation en rafale")
axes[1].legend()
axes[1].axhline(y=50, color="gray", linestyle="--", alpha=0.5, linewidth=1)
plt.suptitle("Simulation Token Bucket — Rate Limiting par Plan", fontsize=13, fontweight="bold")
plt.show()
Diagramme d’architecture API Gateway#
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import seaborn as sns
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
fig, ax = plt.subplots(figsize=(14, 8))
ax.set_xlim(0, 14)
ax.set_ylim(0, 8)
ax.axis("off")
def draw_box(ax, x, y, w, h, label, sublabel="", color="#4C72B0", text_color="white", fontsize=10):
box = mpatches.FancyBboxPatch(
(x, y), w, h,
boxstyle="round,pad=0.1",
facecolor=color, edgecolor="white", linewidth=1.5
)
ax.add_patch(box)
if sublabel:
ax.text(x + w/2, y + h/2 + 0.18, label,
ha="center", va="center", color=text_color,
fontsize=fontsize, fontweight="bold")
ax.text(x + w/2, y + h/2 - 0.22, sublabel,
ha="center", va="center", color=text_color, fontsize=8, style="italic")
else:
ax.text(x + w/2, y + h/2, label,
ha="center", va="center", color=text_color,
fontsize=fontsize, fontweight="bold")
def draw_arrow(ax, x1, y1, x2, y2, label="", color="#555555"):
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle="->", color=color, lw=1.8))
if label:
mx, my = (x1 + x2) / 2, (y1 + y2) / 2
ax.text(mx, my + 0.15, label, ha="center", va="bottom",
fontsize=8, color=color)
# Clients
client_color = "#DD8452"
draw_box(ax, 0.2, 5.8, 1.6, 0.9, "Mobile", "iOS / Android", client_color, fontsize=9)
draw_box(ax, 0.2, 4.4, 1.6, 0.9, "Web SPA", "React / Vue", client_color, fontsize=9)
draw_box(ax, 0.2, 3.0, 1.6, 0.9, "Partenaire", "API externe", client_color, fontsize=9)
# Gateway
gw_color = "#55A868"
draw_box(ax, 2.8, 3.0, 2.8, 4.0, "API Gateway", "", gw_color, fontsize=11)
# Composants internes du gateway
gw_inner_color = "#1a7a40"
inner_items = [
(3.0, 6.5, "TLS Termination"),
(3.0, 5.8, "Authentification JWT"),
(3.0, 5.1, "Rate Limiting"),
(3.0, 4.4, "Routing"),
(3.0, 3.7, "Tracing / Logs"),
(3.0, 3.1, "Transformation"),
]
for ix, iy, ilabel in inner_items:
box = mpatches.FancyBboxPatch((ix, iy), 2.3, 0.55,
boxstyle="round,pad=0.05", facecolor=gw_inner_color,
edgecolor="white", linewidth=0.8, alpha=0.85)
ax.add_patch(box)
ax.text(ix + 1.15, iy + 0.275, ilabel, ha="center", va="center",
color="white", fontsize=8)
# Services backend
svc_color = "#4C72B0"
draw_box(ax, 9.0, 6.2, 2.2, 0.9, "users-service", ":8001", svc_color, fontsize=9)
draw_box(ax, 9.0, 4.8, 2.2, 0.9, "products-service", ":8002", svc_color, fontsize=9)
draw_box(ax, 9.0, 3.4, 2.2, 0.9, "orders-service", ":8003", svc_color, fontsize=9)
draw_box(ax, 9.0, 2.0, 2.2, 0.9, "auth-service", ":9000", "#8172B2", fontsize=9)
# Observabilité
obs_color = "#C44E52"
draw_box(ax, 12.0, 5.5, 1.7, 0.8, "Prometheus", "métriques", obs_color, fontsize=8)
draw_box(ax, 12.0, 4.3, 1.7, 0.8, "Jaeger", "tracing", obs_color, fontsize=8)
draw_box(ax, 12.0, 3.1, 1.7, 0.8, "Loki", "logs", obs_color, fontsize=8)
# Flèches clients → gateway
for cy in [6.25, 4.85, 3.45]:
draw_arrow(ax, 1.8, cy, 2.8, cy, color="#888888")
# Flèches gateway → services
for sy in [6.65, 5.25, 3.85]:
draw_arrow(ax, 5.6, sy, 9.0, sy, color="#888888")
# Flèche gateway → auth (introspection)
draw_arrow(ax, 4.2, 3.0, 4.2, 2.5, color="#8172B2")
draw_arrow(ax, 4.2, 2.5, 9.0, 2.45, color="#8172B2")
# Flèches gateway → observabilité
for oy in [5.9, 4.7, 3.5]:
draw_arrow(ax, 5.6, oy, 12.0, oy, color="#C44E52")
ax.text(7.0, 0.5, "← Réseau interne (privé) →", ha="center", va="center",
fontsize=9, color="#555555", style="italic")
ax.axvline(x=2.5, color="#aaaaaa", linestyle="--", linewidth=0.8, ymin=0.1, ymax=0.95)
ax.text(1.25, 0.5, "Internet", ha="center", fontsize=9, color="#888888")
ax.text(7.5, 0.5, "", ha="center", fontsize=9, color="#888888")
ax.set_title("Architecture API Gateway — flux et responsabilités",
fontsize=13, fontweight="bold", pad=12)
plt.show()
Comparaison des solutions gateway#
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
solutions = ["Kong", "Traefik", "AWS API\nGateway", "Nginx"]
criteres = [
"Facilité\nde config",
"Extensibilité\n(plugins)",
"Cloud-native\n(K8s)",
"Performance\nbrute",
"Observabilité\nnative",
"Coût\nopérationnel",
]
# Scores subjectifs /10 (opinion commune de la communauté)
scores = np.array([
[6, 9, 7, 8, 8, 6], # Kong
[8, 7, 9, 7, 7, 8], # Traefik
[7, 6, 8, 6, 9, 4], # AWS API Gateway (coût élevé = score bas)
[5, 4, 5, 9, 5, 9], # Nginx (simple mais peu extensible)
])
x = np.arange(len(criteres))
width = 0.2
colors = sns.color_palette("muted", 4)
fig, ax = plt.subplots(figsize=(13, 6))
for i, (solution, score) in enumerate(zip(solutions, scores)):
bars = ax.bar(x + i * width - 1.5 * width, score, width,
label=solution.replace("\n", " "), color=colors[i], alpha=0.85)
ax.set_xticks(x)
ax.set_xticklabels(criteres, fontsize=10)
ax.set_ylabel("Score / 10")
ax.set_ylim(0, 12)
ax.set_title("Comparaison des solutions API Gateway (scores indicatifs /10)", fontweight="bold")
ax.legend(title="Solution", loc="upper right")
ax.axhline(y=7, color="gray", linestyle="--", alpha=0.4, linewidth=1)
ax.text(len(criteres) - 0.3, 7.2, "Seuil 7/10", color="gray", fontsize=8)
plt.show()
Résumé#
L’API Gateway est un composant structurant de l’architecture microservices. Il centralise les préoccupations transversales (authentification, rate limiting, logging, tracing) qui seraient coûteuses à implémenter dans chaque service. Le routing path-based, host-based ou header-based permet de diriger le trafic vers les bons services sans que les clients ne connaissent la topologie interne. La validation JWT au gateway, couplée à l’injection de headers d’identité, simplifie les services backend qui reçoivent une identité déjà vérifiée. Le rate limiting par plan protège l’infrastructure contre les abus tout en permettant une monétisation différenciée.
Kong et Traefik couvrent la majorité des besoins on-premise et cloud-native ; AWS API Gateway simplifie le déploiement dans l’écosystème AWS au prix d’un vendor lock-in. La frontière avec le service mesh est claire : le gateway gère le trafic nord-sud (externe → interne), le mesh gère le trafic est-ouest (service → service) — les deux coexistent et se complètent dans les architectures matures.