Backend Development5 min read

API Gateway & BFF Pattern

Design API gateways and Backend-for-Frontend: request routing, auth aggregation, response shaping, rate limiting, and the BFF pattern for mobile vs web.

api-gatewaybffmicroservicesbackend

An API Gateway is a single entry point for client requests, handling cross-cutting concerns: auth, rate limiting, request routing, and response aggregation. The BFF (Backend-for-Frontend) pattern extends this - a dedicated API layer per client type (mobile, web, third-party) that shapes data to each client's needs.

Use an API gateway for microservices - it prevents each service from re-implementing auth, logging, and rate limiting. Use the BFF pattern when your mobile app and web app have significantly different data needs. Avoid gateway-heavy logic - keep business logic in services.

Workflow

  1. Configure request routing and load balancingThe gateway routes requests to the correct backend service based on path, method, or headers.
  2. Centralize authentication and authorizationEach service re-implementing auth creates inconsistency and security risk.
  3. Implement rate limiting at the gatewayGateway-level rate limiting protects all services without each implementing it separately.
  4. Implement the BFF patternMobile and web clients have different payload requirements - BFFs prevent over-fetching and under-fetching.
  5. Add circuit breakers and observabilityCircuit breakers prevent cascade failures when a service is down; observability enables fast debugging.

Output Format

Route-based gateway, JWT validation + forwarded headers, Redis rate limiter with 429 response, BFF per client type with data shaping, circuit breaker with fallback, distributed trace ID propagation.

Examples

BFF aggregation: `async function getDashboard(userId: string): Promise<MobileDashboard> { const [user, orders, notifications] = await Promise.all([userService.getUser(userId), orderService.getRecentOrders(userId, 5), notificationService.getUnread(userId)]); return { name: user.firstName, orderCount: orders.total, unread: notifications.count, thumbnail: user.avatarThumbnailUrl } // mobile-optimized response }`
Circuit breaker: `const breaker = new CircuitBreaker(inventoryService.check, { timeout: 2000, fallback: () => ({ inStock: null, error: "Inventory unavailable" }) }); router.get("/api/product/:id/stock", (req, res) => breaker.fire(req.params.id).then(result => res.json(result)))`
Rate limit: `const rateLimit = async (userId: string, limitPerMin: number) => { const key = `rl:${userId}:${Math.floor(Date.now() / 60000)}`; const count = await redis.incr(key); if (count === 1) await redis.expire(key, 60); return count <= limitPerMin }`

Edge Cases & Gotchas

  • Gateway as a single point of failure: deploy the gateway with high availability (multiple instances, health checks, auto-restart). The gateway failing takes down all services
  • Versioning at the gateway: the gateway can route `/v1/*` to stable services and `/v2/*` to updated services without clients changing - enables gradual migration
  • WebSocket through the gateway: WebSocket connections require special gateway configuration - standard HTTP gateways don't support them. Use dedicated WebSocket gateways (AWS API Gateway WebSocket, Nginx stream)

When to Break These Rules

  • For a single monolithic service or a small number of services (2-3), a gateway adds complexity without benefit. Add one when you have 5+ services with cross-cutting concerns that need centralization.

Reference Files

  • BFF pattern: https://samnewman.io/patterns/architectural/bff/
  • Kong API Gateway: https://docs.konghq.com/
  • AWS API Gateway: https://aws.amazon.com/api-gateway/
  • Opossum circuit breaker: https://github.com/nodeshift/opossum

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.