API Gateway Patterns: The Front Door to Your Microservices
Essential API gateway patterns including routing, authentication, rate limiting, and request transformation for microservices architectures.
February 12, 2026 · 6 min · 1180 words · Rob Washington
Table of Contents
Every request to your microservices should pass through a single front door. That door is your API gateway—and getting it right determines whether your architecture scales gracefully or collapses under complexity.
# FastAPI gateway with JWT validationfromfastapiimportFastAPI,Request,HTTPException,Dependsfromfastapi.securityimportHTTPBearerimporthttpximportjwtapp=FastAPI()security=HTTPBearer()JWT_SECRET="your-secret-key"SERVICES={"users":"http://users-service:8080","orders":"http://orders-service:8080",}asyncdefverify_token(credentials=Depends(security)):try:payload=jwt.decode(credentials.credentials,JWT_SECRET,algorithms=["HS256"])returnpayloadexceptjwt.InvalidTokenError:raiseHTTPException(status_code=401,detail="Invalid token")@app.api_route("/{service}/{path:path}",methods=["GET","POST","PUT","DELETE"])asyncdefgateway(service:str,path:str,request:Request,user:dict=Depends(verify_token)):ifservicenotinSERVICES:raiseHTTPException(status_code=404,detail="Service not found")# Forward request with user identity headerasyncwithhttpx.AsyncClient()asclient:response=awaitclient.request(method=request.method,url=f"{SERVICES[service]}/{path}",headers={"X-User-ID":user["sub"],"X-User-Email":user.get("email",""),},content=awaitrequest.body(),)returnResponse(content=response.content,status_code=response.status_code,headers=dict(response.headers))
# Redis-based rate limitingimportredisimporttimeredis_client=redis.Redis(host='localhost',port=6379)defrate_limit(key:str,limit:int,window:int)->bool:"""
Sliding window rate limiter.
Returns True if request is allowed, False if rate limited.
"""now=time.time()window_start=now-windowpipe=redis_client.pipeline()# Remove old entriespipe.zremrangebyscore(key,0,window_start)# Count requests in windowpipe.zcard(key)# Add current requestpipe.zadd(key,{str(now):now})# Set expirypipe.expire(key,window)results=pipe.execute()request_count=results[1]returnrequest_count<limit# Usage in gateway@app.middleware("http")asyncdefrate_limit_middleware(request:Request,call_next):client_ip=request.client.hostkey=f"rate_limit:{client_ip}"ifnotrate_limit(key,limit=100,window=60):returnJSONResponse(status_code=429,content={"error":"Rate limit exceeded"},headers={"Retry-After":"60"})returnawaitcall_next(request)
# Transform legacy API responses to new format@app.get("/api/v2/users/{user_id}")asyncdefget_user_v2(user_id:str):# Call legacy v1 serviceasyncwithhttpx.AsyncClient()asclient:response=awaitclient.get(f"http://legacy-users:8080/users/{user_id}")legacy_data=response.json()# Transform to v2 formatreturn{"id":legacy_data["userId"],"email":legacy_data["emailAddress"],"name":{"first":legacy_data["firstName"],"last":legacy_data["lastName"],},"created_at":legacy_data["createdDate"],"metadata":{"source":"legacy-transform","version":"2.0"}}
# Mobile BFF - optimized for bandwidth@app.get("/mobile/dashboard")asyncdefmobile_dashboard(user:dict=Depends(verify_token)):asyncwithhttpx.AsyncClient()asclient:# Parallel requests to multiple servicesuser_task=client.get(f"{SERVICES['users']}/users/{user['sub']}")orders_task=client.get(f"{SERVICES['orders']}/users/{user['sub']}/recent?limit=5")user_resp,orders_resp=awaitasyncio.gather(user_task,orders_task)# Aggregate and minimize payloadreturn{"user":{"name":user_resp.json()["name"],"avatar":user_resp.json()["avatarUrl"],},"recentOrders":[{"id":o["id"],"total":o["total"],"status":o["status"]}foroinorders_resp.json()["orders"]]}# Web BFF - richer data@app.get("/web/dashboard")asyncdefweb_dashboard(user:dict=Depends(verify_token)):# Return full data for web clients...
An API gateway isn’t just a reverse proxy—it’s the control plane for your entire API surface. Get routing, authentication, and rate limiting right at the gateway, and your services can focus on business logic instead of infrastructure concerns.
Start simple with path-based routing and authentication. Add rate limiting when you need protection. Introduce BFF patterns when client needs diverge significantly.
Your gateway is the first thing clients see. Make it reliable.
📬 Get the Newsletter
Weekly insights on DevOps, automation, and CLI mastery. No spam, unsubscribe anytime.