API Gateway / BFF — Single Entry Point and Client-Specific Facades
API Gateway = one entry point for all clients (auth, routing, rate limiting). BFF = one gateway per client type with client-optimized response shapes.
When to use
- API Gateway: centralize cross-cutting concerns (auth, rate limiting, routing, SSL termination)
- BFF: mobile and web need different response shapes or protocols
- BFF: frontend team wants to own aggregation logic
Tradeoffs
- Single point of failure if not HA (for API Gateway)
- BFF multiplies maintenance — one per client type
- Go
- Python
func gatewayMiddleware(next http.Handler) http.Handler {
return authMiddleware(rateLimitMiddleware(next))
}
func bffHandler(w http.ResponseWriter, r *http.Request) {
orders := fetchJSON("http://order-service/orders")
users := fetchJSON("http://user-service/profile")
json.NewEncoder(w).Encode(map[string]any{
"orders": orders, "user": users,
})
}
from fastapi import FastAPI, Request
import httpx
app = FastAPI()
async def auth_middleware(request: Request, call_next):
if not request.headers.get("Authorization"):
return Response("Unauthorized", status_code=401)
return await call_next(request)
@app.get("/bff/dashboard")
async def bff_dashboard():
async with httpx.AsyncClient() as client:
orders = (await client.get("http://order-service/orders")).json()
user = (await client.get("http://user-service/profile")).json()
return {"orders": orders, "user": user}
Gotcha: BFF should be owned by the frontend team, not the backend. If backend owns it, it becomes a second API Gateway with extra steps.