MÓDULO 09 · CONCEITO 06 DE 14

API Gateway

autenticação · rate limiting · transformação · BFF · Kong · APIM · YARP

Tempo de leitura ~22 min Pré-requisito Conceito 05 — Reverse Proxy Próximo 07 · Service Mesh

API Gateway vs Reverse Proxy

A distinção é de camada de abstração e consciência de domínio. Um reverse proxy é agnóstico ao conteúdo que trafega — ele encaminha bytes HTTP sem entender o que significam. Um API Gateway entende APIs: sabe que GET /orders/{id} pertence ao serviço de pedidos, que requer o scope orders:read, que a key pública de um usuário tem quota de 1000 req/dia, e que a resposta deve ter o campo customerId mascarado para clientes de certos países.

AspectoReverse ProxyAPI Gateway
CamadaL4/L7 de redeL7 de aplicação
Consciência de domínioNenhuma — encaminha bytesTotal — conhece rotas, métodos, schemas
AutenticaçãoPode validar JWT via móduloNativa — OAuth, API Keys, mTLS, SAML
Rate limitingPor IP ou URLPor usuário, produto, endpoint, plano
TransformaçãoHeaders apenasBody, headers, query params, protocolos
QuotasNãoSim — daily/monthly, por produto
Portal do desenvolvedorNãoSim — registro, docs, sandbox
MonetizaçãoNãoSim — planos pagos, billing
Overhead de latência~0.1-0.5ms~1-10ms (validação de token, lookup de quota)
Exemplosnginx, HAProxy, CaddyKong, AWS API GW, Azure APIM, Apigee
nota A linha se borra na prática: nginx com OpenResty (Lua) pode fazer autenticação JWT e rate limiting por usuário — tecnicamente um API Gateway. Kong é construído sobre nginx+OpenResty. A distinção real está em o que vem pronto: um gateway traz gerenciamento de APIs como primeiro cidadão, com UI administrativa, portal, analytics por rota e plano de preços. Nginx requer scripts customizados para chegar no mesmo lugar.

Padrões de deployment

Edge Gateway — entrada única para APIs externas

O padrão mais comum: um único gateway na borda do sistema recebe todo o tráfego externo. Clientes (mobile, web, parceiros) acessam api.empresa.com e o gateway roteia para os serviços internos corretos. Centraliza autenticação, rate limiting e logging.

Internet
  ↓
api.empresa.com (DNS → Gateway)
  ↓ autenticação, rate limiting, logging
  ├→ /api/orders/*     → orders-service:8080
  ├→ /api/products/*   → products-service:8080
  ├→ /api/customers/*  → customers-service:8080
  └→ /api/payments/*   → payments-service:8080 (PCI zone)
atenção Single point of failure: um gateway centralizado é um SPOF em potencial. Mitigue com: múltiplas instâncias atrás de um load balancer, circuit breakers para cada backend, fallbacks configurados para serviços críticos, e health checks agressivos. O gateway deve ser stateless — sessões e dados de quota em Redis ou outro store externo — para que instâncias sejam substituíveis.

BFF (Backend for Frontend)

O padrão BFF cria um gateway especializado por tipo de cliente — em vez de um gateway genérico para todos. O BFF mobile agrega dados de forma otimizada para telas pequenas com conexão instável; o BFF web pode retornar payloads mais ricos; o BFF de parceiros expõe apenas as APIs com contrato estável.

mobile-app.empresa.com  → BFF Mobile  → orders-service
                                        → products-service (campos reduzidos)
                                        → push-notification-service

web-app.empresa.com     → BFF Web     → orders-service
                                        → products-service (campos completos)
                                        → recommendations-service

partner-api.empresa.com → BFF Partner → orders-service (apenas status)
                                        → products-service (apenas catálogo público)

A vantagem do BFF é que cada equipe (mobile, web, parceiros) tem autonomia para otimizar seu gateway sem afetar os outros. A desvantagem é duplicação de lógica — autenticação, logging e circuit breaking precisam ser implementados em cada BFF.

Internal Gateway — entre microsserviços

Para comunicação service-to-service que precisa de autenticação de serviço, rate limiting por chamador, e observabilidade fina — sem a sobrecarga de um service mesh. Menos comum porque service meshes (Istio, Linkerd) resolvem o mesmo problema de forma mais transparente.

Micro-gateway / Sidecar Gateway

Cada serviço tem seu próprio gateway sidecar que gerencia o tráfego de entrada — autenticação, rate limiting e transformação específicos para aquele serviço. Utilizado quando os requisitos de gateway são muito específicos por serviço para compartilhar uma instância central.

Autenticação e Autorização

O API Gateway é o local natural para validar identidade antes que a requisição chegue ao backend — os serviços internos recebem requisições já autenticadas e podem confiar nos headers injetados pelo gateway (user-id, roles, scopes).

API Keys

O mecanismo mais simples: um token opaco gerado pelo gateway, associado a um cliente (aplicação, parceiro). O gateway valida a key em cada requisição, aplica as quotas e permissões do plano associado, e injeta o identificador do cliente no header X-Consumer-ID para logging.

# Exemplo de fluxo com API Key
Cliente → GET /api/products HTTP/1.1
          Authorization: ApiKey abc123xyz

Gateway:
1. Extrai "abc123xyz" do header Authorization
2. Lookup em Redis/banco: { consumer: "acme-corp", plan: "basic", quota_day: 1000, quota_used: 47 }
3. Verifica quota_used < quota_day
4. Incrementa quota_used atomicamente
5. Injeta headers para o backend:
   X-Consumer-ID: acme-corp
   X-Consumer-Plan: basic
   X-Rate-Limit-Remaining: 953
6. Encaminha para products-service

Backend recebe requisição com X-Consumer-ID — não precisa validar auth

OAuth 2.0 e JWT

Para APIs que autenticam usuários finais — não apenas aplicações — OAuth 2.0 com JWTs é o padrão. O gateway valida o token JWT (assinatura, expiração, issuer, audience) sem consultar o authorization server a cada requisição — a chave pública do AS é cacheada localmente.

# Validação de JWT no gateway (Kong, por exemplo)
# Plugin jwt ou openid-connect

# Fluxo completo:
1. Cliente obtém token do Authorization Server (auth.empresa.com)
   POST /oauth/token → { access_token: "eyJ...", expires_in: 3600 }

2. Cliente chama a API com o token
   GET /api/orders Authorization: Bearer eyJ...

3. Gateway valida localmente:
   - Verifica assinatura com chave pública JWKS (cacheada)
   - Verifica exp (não expirado)
   - Verifica iss = "auth.empresa.com"
   - Verifica aud = "api.empresa.com"
   - Extrai sub, scope, roles do payload

4. Gateway injeta no header para o backend:
   X-User-ID: user-abc123
   X-User-Roles: admin,orders:write
   X-User-Scopes: orders:read orders:write

5. Backend confia nos headers — não revalida o token
atenção Token introspection vs validação local: validar JWT localmente com a chave pública é rápido (sem round-trip) mas não detecta tokens revogados até a expiração. Token introspection (chamar o AS para cada request) detecta revogação imediata mas adiciona latência e carga no AS. Solução comum: JWTs com expiração curta (5-15 min) + refresh tokens de longa duração + lista de revogação (Redis) apenas para tokens comprometidos.

mTLS para APIs de parceiros

Para APIs B2B onde o cliente é uma empresa (não um usuário), mTLS oferece autenticação mútua baseada em certificados — sem senhas ou tokens que podem vazar. O gateway valida o certificado cliente (client certificate) e o associa a um consumer configurado.

# Kong — plugin mTLS auth
plugins:
- name: mtls-auth
  config:
    ca_certificates:
    - id: "uuid-do-certificado-CA-da-empresa-parceira"
    skip_consumer_lookup: false
    authenticated_group_by: "CN"  # usar o Common Name do cert como consumer ID

Autorização — o gateway decide o quê, o backend decide o como

Uma separação saudável: o gateway verifica se o cliente tem permissão para chamar o endpoint (scope orders:write presente no token, plano inclui o recurso). O backend verifica se o usuário tem permissão para a operação específica naquele dado (o pedido pertence ao usuário?). Não delegue decisões de negócio ao gateway — ele não conhece o estado da aplicação.

Rate Limiting e Quotas

Rate limiting no gateway protege os backends de sobrecarga e garante uso justo entre clientes. A implementação correta requer escolher o algoritmo certo e o nível de granularidade.

Algoritmos

Fixed Window Counter

Conta requisições em janelas fixas de tempo (ex: 100 req/minuto). A janela reseta a cada minuto. Simples de implementar com Redis INCR + TTL. Problema: boundary burst — um cliente pode fazer 100 req nos últimos segundos de uma janela e 100 req nos primeiros da próxima, efetivamente passando 200 req em alguns segundos.

# Redis — fixed window
local key = "rate_limit:" .. consumer_id .. ":" .. math.floor(now / 60)
local count = redis.incr(key)
if count == 1 then
    redis.expire(key, 60)  -- TTL de 1 minuto
end
if count > 100 then
    return 429  -- Too Many Requests
end

Sliding Window Log

Armazena o timestamp de cada requisição. Para verificar, conta quantas ocorreram na janela de 1 minuto terminando agora. Preciso — sem boundary burst — mas usa memória proporcional ao número de requisições por usuário.

Sliding Window Counter (aproximação)

Combina dois fixed windows para aproximar o sliding window: count = current_window_count + previous_window_count * (1 - elapsed/window_size). Eficiente em memória, boa aproximação sem armazenar timestamps individuais. É o algoritmo padrão do Redis Rate Limiting e do Kong.

Token Bucket

Um balde tem capacidade máxima de B tokens. Tokens são adicionados à taxa r por segundo. Cada requisição consome 1 token. Se o balde está vazio, a requisição é rejeitada (ou aguarda). Permite bursts até a capacidade do balde — cliente pode usar acumulado de tokens em um pico.

# Token Bucket — pseudocódigo
def check_rate_limit(consumer_id, capacity=100, refill_rate=10):
    now = time.now()
    bucket = redis.get(f"bucket:{consumer_id}") or {tokens: capacity, last_refill: now}

    # Refill tokens baseado no tempo decorrido
    elapsed = now - bucket.last_refill
    new_tokens = min(capacity, bucket.tokens + elapsed * refill_rate)

    if new_tokens < 1:
        return False, 429  # balde vazio

    redis.set(f"bucket:{consumer_id}", {tokens: new_tokens - 1, last_refill: now})
    return True, None

Leaky Bucket

Requisições entram no balde a qualquer taxa, mas saem (são processadas) a uma taxa constante. Excess é rejeitado ou enfileirado. Garante taxa constante de processamento — útil para suavizar bursts antes de chegar ao backend. Menos comum em API Gateways modernos; mais usado em traffic shaping de rede.

Granularidade de rate limiting

O gateway deve suportar múltiplos eixos de limitação simultaneamente:

# Kong — plugin rate-limiting avançado
plugins:
- name: rate-limiting-advanced
  config:
    limit_by: consumer          # limitar por consumer autenticado
    strategy: sliding-window
    limits:
      second: 20                # burst: 20 req/s
      minute: 500               # 500 req/min
      hour: 5000                # 5000 req/h
      day: 10000                # 10000 req/dia
    sync_rate: 10               # sincronizar contadores com Redis a cada 10s
    redis:
      host: redis.internal
      port: 6379
    error_message: "Rate limit excedido. Tente novamente após {reset_at}."
    hide_client_headers: false  # retornar X-RateLimit-* headers ao cliente

Headers de rate limiting

# Headers de resposta padronizados (IETF draft-ietf-httpapi-ratelimit-headers)
X-RateLimit-Limit: 1000          # limite total na janela atual
X-RateLimit-Remaining: 847       # requisições restantes
X-RateLimit-Reset: 1715285400   # Unix timestamp do reset
Retry-After: 60                  # segundos para aguardar (apenas quando 429)

# Quando limite é excedido:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1715285400

{
  "error": "rate_limit_exceeded",
  "message": "Limite de 1000 requisições por hora excedido",
  "retry_after_seconds": 60
}

Transformação de Payload

O gateway pode modificar requisições e respostas em trânsito — adicionando, removendo ou transformando campos, convertendo formatos, e adaptando contratos entre o que o cliente espera e o que o backend oferece.

Transformações comuns

# Kong — plugin request-transformer
plugins:
- name: request-transformer
  config:
    # Adicionar headers antes de encaminhar ao backend
    add:
      headers:
      - "X-Consumer-ID:$(consumer.id)"
      - "X-Request-ID:$(uuid)"
    # Remover headers sensíveis do cliente
    remove:
      headers:
      - "Authorization"   # backend não precisa do token JWT
      - "Cookie"

# Kong — plugin response-transformer
- name: response-transformer
  config:
    remove:
      json:
      - "internal_id"        # não expor ID interno do banco
      - "created_by_user_id" # não expor dado interno
    add:
      headers:
      - "X-API-Version:v1"

# Transformação de formato (REST → SOAP, JSON → XML)
# Geralmente requer scripting Lua (Kong) ou policies (APIM, Apigee)

Protocol translation — REST para gRPC

Um gateway moderno pode expor uma API REST externamente e traduzir para gRPC internamente — chamado de transcoding. O Envoy suporta isso nativamente com anotações google.api.http no .proto. O AWS API Gateway e o Azure APIM têm suporte limitado; Kong requer plugin customizado.

# Envoy como gateway com gRPC transcoding
# O cliente chama REST, o Envoy chama gRPC no backend

# envoy.yaml
http_filters:
- name: envoy.filters.http.grpc_json_transcoder
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
    proto_descriptor: "/etc/envoy/api_descriptor.pb"  # arquivo binário do proto
    services:
    - "orders.v1.OrderService"
    print_options:
      add_whitespace: false
      always_print_primitive_fields: true

# Com isso:
# POST /v1/orders → gRPC OrderService.CreateOrder
# GET /v1/orders/{id} → gRPC OrderService.GetOrder
# (mapeamento via anotações google.api.http no .proto)

Request/Response validation

O gateway pode validar payloads contra um schema OpenAPI antes de encaminhar ao backend — rejeitando requisições malformadas com 400 antes de consumir recursos do serviço. Reduz carga nos backends e centraliza validação básica de formato.

# Kong — plugin request-validator
plugins:
- name: request-validator
  config:
    body_schema: |
      {
        "type": "object",
        "required": ["customer_id", "items"],
        "properties": {
          "customer_id": { "type": "string", "format": "uuid" },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": ["product_id", "quantity"],
              "properties": {
                "product_id": { "type": "string" },
                "quantity": { "type": "integer", "minimum": 1 }
              }
            }
          }
        }
      }
    allowed_content_types:
      application/json: true
    version: draft4

Agregação de Backends

Em vez de o cliente fazer múltiplas requisições paralelas para diferentes serviços, o gateway pode agregar em uma única chamada — reduzindo latência total (especialmente crítico em redes móveis) e simplificando o cliente.

Parallel fan-out

# Cliente faz 1 request, gateway faz 3 em paralelo e combina
GET /api/dashboard → Gateway:
  paralelo:
    GET orders-service/api/recent-orders?user=123     → { orders: [...] }
    GET recommendations-service/api/for-user?id=123  → { products: [...] }
    GET notifications-service/api/unread?user=123     → { count: 5 }
  combina:
  → {
      recent_orders: [...],
      recommendations: [...],
      unread_notifications: 5
    }

# Latência total = max(latências individuais), não soma
# Sem agregação: 80ms + 120ms + 60ms = 260ms
# Com agregação: max(80, 120, 60) = 120ms

Implementação de agregação

Gateways de mercado (Kong Enterprise, Apigee) suportam aggregation via scripts Lua ou JavaScript. Alternativas mais limpas:

dica Agregação no gateway vs BFF em código: para agregações simples e estáveis, o gateway com scripts é suficiente. Para lógica complexa, condicional, ou que muda frequentemente, um BFF em código (C#/Python/Go) é muito mais fácil de testar, versionar e debugar. Não tente fazer lógica de negócio em Lua dentro do Kong — isso cria débito técnico difícil de manter.

Circuit Breaker e Retry no Gateway

O gateway pode implementar circuit breaker para proteger clientes quando backends estão degradados — em vez de deixar requisições pendentes por 30 segundos antes de timeout, o gateway responde rapidamente com 503 quando o backend está com falha.

# Envoy — circuit breaker por cluster
clusters:
- name: orders_cluster
  circuit_breakers:
    thresholds:
    - priority: DEFAULT
      max_connections: 100          # máx. conexões simultâneas
      max_pending_requests: 50      # máx. requisições na fila
      max_requests: 200             # máx. requisições simultâneas
      max_retries: 3                # máx. retries simultâneos
      # quando limites são excedidos, Envoy retorna 503 imediatamente

# Envoy — outlier detection (passive circuit breaker)
  outlier_detection:
    consecutive_5xx: 5             # 5 erros 5xx consecutivos → ejetar instância
    interval: 10s
    base_ejection_time: 30s        # tempo mínimo fora do pool
    max_ejection_percent: 50       # no máximo 50% do pool pode ser ejetado

# Retry policy
  retry_policy:
    retry_on: "5xx,reset,connect-failure,retriable-4xx"
    num_retries: 2
    per_try_timeout: 5s
    retry_host_predicate:
    - name: envoy.retry_host_predicates.previous_hosts  # evitar host que já falhou

Retry only quando seguro

O gateway só deve fazer retry em operações idempotentes ou quando tem certeza de que o backend não processou a requisição (ex: falha de conexão antes de enviar). Retry cego em POST pode duplicar criações. Configure retry apenas para: GET/HEAD/OPTIONS (sempre idempotentes), conexões que falharam antes de chegar ao backend, e erros 503 com header Retry-After.

Portal do Desenvolvedor

Um API Gateway completo inclui um portal onde desenvolvedores externos podem descobrir APIs, gerar credenciais, testar endpoints em sandbox, e acompanhar uso e quotas. É a camada de produto da API — sem ela, uma API pública é apenas uma URL sem documentação.

Componentes de um portal maduro

Gestão do ciclo de vida da API

# Versionamento no gateway — múltiplas versões coexistindo
routes:
- name: orders-v1
  paths: ["/api/v1/orders"]
  service: orders-service-v1
  plugins:
  - name: response-transformer
    config:
      add.headers: ["Sunset: Sat, 31 Dec 2026 00:00:00 GMT",
                    "Deprecation: true",
                    "Link: </api/v2/orders>; rel=\"successor-version\""]

- name: orders-v2
  paths: ["/api/v2/orders"]
  service: orders-service-v2

# Quando v1 é deprecada:
# 1. Adicionar header Sunset com data de desativação
# 2. Logar consumidores ainda usando v1
# 3. Contactar consumidores com uso significativo
# 4. Desativar v1 na data de sunset

Soluções de Mercado

Kong Gateway (open source + enterprise)

Construído sobre nginx+OpenResty (Lua), é o API Gateway open source mais usado. A versão gratuita inclui roteamento, rate limiting, autenticação básica, plugins da comunidade. A versão Enterprise adiciona portal do desenvolvedor, RBAC granular, analytics avançado e suporte comercial. Configurável via Admin API REST, Deck (declarativo como código) ou KIC (Kubernetes Ingress Controller).

# Kong declarativo com Deck (deck.yaml)
services:
- name: orders-service
  url: http://orders-service:8080
  routes:
  - name: orders-route
    paths: ["/api/v1/orders"]
    methods: [GET, POST, PATCH, DELETE]
    strip_path: false
  plugins:
  - name: jwt
    config:
      secret_is_base64: false
      claims_to_verify: [exp, nbf]
  - name: rate-limiting
    config:
      minute: 1000
      hour: 10000
      policy: redis
      redis_host: redis.internal
  - name: correlation-id
    config:
      header_name: X-Request-ID
      generator: uuid#counter
  - name: prometheus  # métricas para Grafana/Prometheus
    config:
      per_consumer: true

AWS API Gateway

Serviço gerenciado da AWS — sem infraestrutura para operar. Dois tipos: REST API (mais completo, mais caro) e HTTP API (mais simples, mais barato, menor latência). Integra nativamente com Lambda, ECS, EKS, Cognito, WAF e CloudWatch. Rate limiting e throttling configurados via usage plans.

# AWS API Gateway via CDK (TypeScript)
const api = new RestApi(this, 'OrdersApi', {
  restApiName: 'Orders Service',
  defaultCorsPreflightOptions: {
    allowOrigins: Cors.ALL_ORIGINS,
    allowMethods: ['GET', 'POST', 'PATCH'],
  },
});

// Authorizer JWT com Cognito
const authorizer = new CognitoUserPoolsAuthorizer(this, 'Authorizer', {
  cognitoUserPools: [userPool],
});

// Rota com authorizer e integração Lambda
const orders = api.root.addResource('orders');
orders.addMethod('POST', new LambdaIntegration(createOrderFn), {
  authorizer,
  authorizationType: AuthorizationType.COGNITO,
});

// Usage plan — rate limiting e quotas
const plan = api.addUsagePlan('BasicPlan', {
  throttle: { rateLimit: 100, burstLimit: 200 },
  quota: { limit: 10000, period: Period.DAY },
});
plan.addApiStage({ stage: api.deploymentStage });

// API Key para acesso
const key = api.addApiKey('PartnerKey');
plan.addApiKey(key);

Azure API Management (APIM)

Solução enterprise da Microsoft com portal do desenvolvedor completo, políticas XML expressivas, integração com Azure AD e RBAC, suporte a mocking, e analytics nativo. Curva de aprendizado maior que Kong, mas integração excepcional com o ecossistema Azure.

<!-- Azure APIM — policies XML -->
<policies>
  <inbound>
    <!-- Validar JWT do Azure AD -->
    <validate-jwt header-name="Authorization" failed-validation-httpcode="401">
      <openid-config url="https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration"/>
      <audiences><audience>api://orders-api</audience></audiences>
      <required-claims>
        <claim name="roles" match="any">
          <value>Orders.Read</value>
          <value>Orders.Write</value>
        </claim>
      </required-claims>
    </validate-jwt>

    <!-- Rate limiting por subscription key -->
    <rate-limit-by-key calls="100" renewal-period="60"
      counter-key="@(context.Subscription.Id)" />

    <!-- Injetar correlation ID -->
    <set-header name="X-Request-ID" exists-action="skip">
      <value>@(Guid.NewGuid().ToString())</value>
    </set-header>
  </inbound>

  <backend>
    <retry condition="@(context.Response.StatusCode == 503)" count="2" interval="1">
      <forward-request timeout="30" />
    </retry>
  </backend>

  <outbound>
    <!-- Remover headers internos da resposta -->
    <set-header name="X-Internal-Server" exists-action="delete"/>
    <!-- Adicionar rate limit headers -->
    <set-header name="X-RateLimit-Remaining">
      <value>@(context.Response.Headers.GetValueOrDefault("X-RateLimit-Remaining","")</value>
    </set-header>
  </outbound>
</policies>

Comparativo de soluções

SoluçãoModeloMelhor paraCuidados
Kong OSSSelf-hosted, open sourceControle total, budget limitado, muita customizaçãoOperação própria, HA manual
Kong EnterpriseSelf-hosted ou CloudPortal completo, RBAC, suporte comercialCusto de licença elevado
AWS API GWServerless SaaSAWS-native, Lambda, sem opsVendor lock-in, custo por requisição
Azure APIMSaaS (PaaS)Azure AD, enterprise, portal robustoProvisionamento lento, custo alto
Apigee (Google)SaaSAPIs públicas, analytics avançado, monetizaçãoComplexidade, custo, Google Cloud dependência
TraefikSelf-hosted, open sourceKubernetes-native, discovery automático, simplicidadePortal e analytics limitados vs Kong

API Gateway vs Service Mesh

Uma confusão comum: API Gateway e Service Mesh parecem resolver problemas similares (observabilidade, autenticação, retry), mas operam em camadas e escopos diferentes.

AspectoAPI GatewayService Mesh
TráfegoNorte-Sul (externo → interno)Leste-Oeste (serviço → serviço)
AudiênciaClientes externos, parceirosServiços internos
AutenticaçãoAPI Keys, OAuth, JWT de usuáriosmTLS entre serviços, SPIFFE/SPIRE
Rate limitingPor consumidor, por planoPor serviço chamador
DeploymentCentralizado (1 instância/cluster)Descentralizado (sidecar em cada pod)
ProtocoloHTTP/REST, GraphQL, gRPC (via transcoding)Qualquer TCP — gRPC, HTTP, banco de dados
Complexidade opsModeradaAlta (plano de controle, sidecars, certificados)

Em arquiteturas maduras, ambos coexistem: o API Gateway gerencia a entrada do tráfego externo, e o service mesh gerencia a comunicação interna entre serviços. O próximo conceito explora service meshes em profundidade.

Comparação por linguagem

Implementação de um BFF gateway leve em código — o padrão alternativo a soluções de mercado quando a lógica de agregação é complexa demais para configuração declarativa.

C# — YARP (Yet Another Reverse Proxy)
// YARP é a biblioteca Microsoft para reverse proxy / gateway em .NET
// Permite roteamento, transformação e autenticação em código C#

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(transforms =>
    {
        // Transformação customizada — injeta X-Consumer-ID após validar JWT
        transforms.AddRequestTransform(async ctx =>
        {
            var user = ctx.HttpContext.User;
            if (user.Identity?.IsAuthenticated == true)
            {
                var userId = user.FindFirst("sub")?.Value;
                ctx.ProxyRequest.Headers.TryAddWithoutValidation(
                    "X-Consumer-ID", userId);
                // Remover Authorization antes de encaminhar ao backend
                ctx.ProxyRequest.Headers.Remove("Authorization");
            }
        });

        // Adicionar correlation ID
        transforms.AddRequestTransform(ctx =>
        {
            var requestId = ctx.HttpContext.TraceIdentifier;
            ctx.ProxyRequest.Headers.TryAddWithoutValidation(
                "X-Request-ID", requestId);
            return ValueTask.CompletedTask;
        });
    });

// Rate limiting com ASP.NET Core
builder.Services.AddRateLimiter(options =>
{
    options.AddPolicy("per-user", context =>
    {
        var userId = context.User.FindFirst("sub")?.Value ?? context.Connection.RemoteIpAddress?.ToString() ?? "anonymous";
        return RateLimitPartition.GetSlidingWindowLimiter(userId, _ =>
            new SlidingWindowRateLimiterOptions
            {
                PermitLimit = 100,
                Window = TimeSpan.FromMinutes(1),
                SegmentsPerWindow = 4,
                QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                QueueLimit = 10,
            });
    });
    options.OnRejected = async (context, token) =>
    {
        context.HttpContext.Response.StatusCode = 429;
        await context.HttpContext.Response.WriteAsJsonAsync(new
        {
            error = "rate_limit_exceeded",
            retry_after_seconds = 60
        }, token);
    };
});

// BFF endpoint de agregação — combina múltiplos serviços
builder.Services.AddHttpClient("orders", c => c.BaseAddress = new Uri("http://orders-service"));
builder.Services.AddHttpClient("products", c => c.BaseAddress = new Uri("http://products-service"));

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimiter();

// Endpoint de agregação customizado
app.MapGet("/api/dashboard", async (
    IHttpClientFactory factory,
    HttpContext ctx) =>
{
    var userId = ctx.User.FindFirst("sub")?.Value!;
    var ordersClient = factory.CreateClient("orders");
    var productsClient = factory.CreateClient("products");

    // Fan-out paralelo
    var ordersTask = ordersClient.GetFromJsonAsync<OrdersResponse>(
        $"/internal/orders/recent?userId={userId}",
        ctx.RequestAborted);
    var recommendationsTask = productsClient.GetFromJsonAsync<RecommendationsResponse>(
        $"/internal/recommendations?userId={userId}",
        ctx.RequestAborted);

    await Task.WhenAll(ordersTask, recommendationsTask);

    return Results.Ok(new
    {
        recent_orders = ordersTask.Result?.Orders,
        recommendations = recommendationsTask.Result?.Products,
    });
})
.RequireAuthorization()
.RequireRateLimiting("per-user");

// Proxy YARP para rotas genéricas
app.MapReverseProxy();
app.Run();

// appsettings.json — configuração do YARP
// {
//   "ReverseProxy": {
//     "Routes": {
//       "orders-route": {
//         "ClusterId": "orders-cluster",
//         "Match": { "Path": "/api/v1/orders/{**catch-all}" },
//         "AuthorizationPolicy": "default"
//       }
//     },
//     "Clusters": {
//       "orders-cluster": {
//         "Destinations": {
//           "orders-1": { "Address": "http://orders-1:8080" },
//           "orders-2": { "Address": "http://orders-2:8080" }
//         },
//         "LoadBalancingPolicy": "LeastRequests",
//         "HealthCheck": { "Active": { "Enabled": true, "Path": "/ready" } }
//       }
//     }
//   }
// }

YARP é o proxy reverso oficial da Microsoft para ASP.NET Core — transforma qualquer aplicação .NET em um gateway programável com toda a expressividade do C# para transformações e lógica de roteamento complexa.

Python — FastAPI como BFF gateway
import asyncio
import httpx
from fastapi import FastAPI, Depends, HTTPException, Request, Response
from fastapi.security import OAuth2PasswordBearer
from jose import jwt, JWTError
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded

app = FastAPI(title="BFF Gateway")

# Rate limiting com slowapi (baseado em limits)
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)

# Clientes HTTP reutilizáveis (connection pool)
orders_client = httpx.AsyncClient(
    base_url="http://orders-service",
    timeout=10.0,
    limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
)
products_client = httpx.AsyncClient(
    base_url="http://products-service",
    timeout=10.0,
)

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
JWKS_URL = "https://auth.empresa.com/.well-known/jwks.json"
_jwks_cache = None

async def get_current_user(token: str = Depends(oauth2_scheme)) -> dict:
    try:
        # Validar JWT localmente com chave pública cacheada
        payload = jwt.decode(
            token,
            await get_jwks(),
            algorithms=["RS256"],
            audience="api.empresa.com",
        )
        return payload
    except JWTError as e:
        raise HTTPException(status_code=401, detail=f"Token inválido: {e}")

# Middleware — injetar correlation ID e logar requisições
@app.middleware("http")
async def observability_middleware(request: Request, call_next):
    import uuid
    request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
    request.state.request_id = request_id

    response = await call_next(request)
    response.headers["X-Request-ID"] = request_id
    return response

# Endpoint de agregação — dashboard
@app.get("/api/dashboard")
@limiter.limit("60/minute")  # rate limit por IP (substituir por user ID em prod)
async def get_dashboard(
    request: Request,
    user: dict = Depends(get_current_user),
):
    user_id = user["sub"]

    # Fan-out paralelo para múltiplos serviços
    orders_task = orders_client.get(
        f"/internal/orders/recent",
        params={"user_id": user_id},
        headers={"X-Consumer-ID": user_id},
    )
    recommendations_task = products_client.get(
        f"/internal/recommendations",
        params={"user_id": user_id},
        headers={"X-Consumer-ID": user_id},
    )

    orders_resp, recs_resp = await asyncio.gather(
        orders_task,
        recommendations_task,
        return_exceptions=True,  # não propaga exceção de um para o outro
    )

    # Tratamento de falha parcial — degradar graciosamente
    orders = []
    if isinstance(orders_resp, httpx.Response) and orders_resp.status_code == 200:
        orders = orders_resp.json().get("orders", [])

    recommendations = []
    if isinstance(recs_resp, httpx.Response) and recs_resp.status_code == 200:
        recommendations = recs_resp.json().get("products", [])

    return {
        "user_id": user_id,
        "recent_orders": orders,
        "recommendations": recommendations,
    }

# Proxy genérico para outros endpoints — encaminha com autenticação validada
@app.api_route(
    "/api/v1/orders/{path:path}",
    methods=["GET", "POST", "PATCH", "DELETE"]
)
@limiter.limit("100/minute")
async def proxy_orders(
    request: Request,
    path: str,
    user: dict = Depends(get_current_user),
):
    # Encaminhar para o backend com consumer ID injetado
    upstream_url = f"/v1/orders/{path}"
    body = await request.body()

    upstream_response = await orders_client.request(
        method=request.method,
        url=upstream_url,
        content=body,
        headers={
            "Content-Type": request.headers.get("Content-Type", "application/json"),
            "X-Consumer-ID": user["sub"],
            "X-Consumer-Roles": ",".join(user.get("roles", [])),
        },
        params=dict(request.query_params),
    )

    return Response(
        content=upstream_response.content,
        status_code=upstream_response.status_code,
        headers=dict(upstream_response.headers),
    )

@app.on_event("shutdown")
async def shutdown():
    await orders_client.aclose()
    await products_client.aclose()

FastAPI como BFF gateway usa asyncio.gather para fan-out paralelo e return_exceptions=True para degradação graciosa — falha de um serviço não cancela os outros.

Go — gateway com net/http e middleware chain
// gateway/main.go
package main

import (
    "context"
    "encoding/json"
    "net/http"
    "net/http/httputil"
    "net/url"
    "sync"
    "time"

    "github.com/golang-jwt/jwt/v5"
    "golang.org/x/time/rate"
)

type Gateway struct {
    ordersProxy   *httputil.ReverseProxy
    productsProxy *httputil.ReverseProxy
    ordersClient  *http.Client
    limiterMap    sync.Map // consumer_id → *rate.Limiter
    jwksCache     *JWKSCache
}

func NewGateway(ordersURL, productsURL string) *Gateway {
    ordersTarget, _ := url.Parse(ordersURL)
    productsTarget, _ := url.Parse(productsURL)

    transport := &http.Transport{
        MaxIdleConns:        200,
        MaxIdleConnsPerHost: 20,
        IdleConnTimeout:     90 * time.Second,
    }

    ordersProxy := httputil.NewSingleHostReverseProxy(ordersTarget)
    ordersProxy.Transport = transport

    // Modificar request antes de encaminhar
    ordersProxy.Director = func(req *http.Request) {
        req.URL.Scheme = ordersTarget.Scheme
        req.URL.Host = ordersTarget.Host
        req.Host = ordersTarget.Host
        req.Header.Del("Authorization") // não encaminhar token JWT ao backend
    }

    return &Gateway{
        ordersProxy:  ordersProxy,
        ordersClient: &http.Client{Transport: transport, Timeout: 10 * time.Second},
        jwksCache:    NewJWKSCache("https://auth.empresa.com/.well-known/jwks.json"),
    }
}

// Middleware de autenticação JWT
func (g *Gateway) AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        tokenStr := extractBearerToken(r)
        if tokenStr == "" {
            writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "token ausente"})
            return
        }

        claims, err := g.jwksCache.ValidateToken(tokenStr)
        if err != nil {
            writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "token inválido"})
            return
        }

        // Injetar claims no contexto para handlers
        ctx := context.WithValue(r.Context(), claimsKey, claims)
        r = r.WithContext(ctx)

        // Injetar header para backends internos
        r.Header.Set("X-Consumer-ID", claims.Subject)

        next.ServeHTTP(w, r)
    })
}

// Middleware de rate limiting por consumer (token bucket)
func (g *Gateway) RateLimitMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        claims := r.Context().Value(claimsKey).(*Claims)
        consumerID := claims.Subject

        limiter := g.getLimiter(consumerID)
        if !limiter.Allow() {
            w.Header().Set("Retry-After", "60")
            writeJSON(w, http.StatusTooManyRequests, map[string]string{
                "error":   "rate_limit_exceeded",
                "message": "Limite de requisições excedido",
            })
            return
        }

        next.ServeHTTP(w, r)
    })
}

func (g *Gateway) getLimiter(consumerID string) *rate.Limiter {
    // Token bucket: 100 req/min = ~1.67 req/s, burst de 20
    if v, ok := g.limiterMap.Load(consumerID); ok {
        return v.(*rate.Limiter)
    }
    l := rate.NewLimiter(rate.Every(600*time.Millisecond), 20) // ~100/min, burst 20
    g.limiterMap.Store(consumerID, l)
    return l
}

// Handler de agregação — fan-out paralelo
func (g *Gateway) DashboardHandler(w http.ResponseWriter, r *http.Request) {
    claims := r.Context().Value(claimsKey).(*Claims)
    userID := claims.Subject

    type result struct {
        data interface{}
        err  error
    }

    ordersCh := make(chan result, 1)
    recsCh := make(chan result, 1)

    // Fan-out paralelo
    go func() {
        resp, err := g.ordersClient.Get(
            "http://orders-service/internal/orders/recent?user_id=" + userID)
        if err != nil {
            ordersCh <- result{err: err}
            return
        }
        defer resp.Body.Close()
        var data map[string]interface{}
        json.NewDecoder(resp.Body).Decode(&data)
        ordersCh <- result{data: data["orders"]}
    }()

    go func() {
        resp, err := g.ordersClient.Get(
            "http://products-service/internal/recommendations?user_id=" + userID)
        if err != nil {
            recsCh <- result{err: err}
            return
        }
        defer resp.Body.Close()
        var data map[string]interface{}
        json.NewDecoder(resp.Body).Decode(&data)
        recsCh <- result{data: data["products"]}
    }()

    // Coletar com timeout
    ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
    defer cancel()

    var orders, recommendations interface{}
    for i := 0; i < 2; i++ {
        select {
        case res := <-ordersCh:
            if res.err == nil {
                orders = res.data
            }
        case res := <-recsCh:
            if res.err == nil {
                recommendations = res.data
            }
        case <-ctx.Done():
            // timeout — retornar o que já temos
        }
    }

    writeJSON(w, http.StatusOK, map[string]interface{}{
        "user_id":         userID,
        "recent_orders":   orders,
        "recommendations": recommendations,
    })
}

func main() {
    gw := NewGateway(
        "http://orders-service:8080",
        "http://products-service:8080",
    )

    mux := http.NewServeMux()
    mux.HandleFunc("/api/dashboard", gw.DashboardHandler)
    mux.Handle("/api/v1/orders/", gw.ordersProxy)

    // Chain de middlewares: Auth → RateLimit → Handler
    handler := gw.AuthMiddleware(gw.RateLimitMiddleware(mux))

    server := &http.Server{
        Addr:         ":8080",
        Handler:      handler,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 30 * time.Second,
        IdleTimeout:  90 * time.Second,
    }

    server.ListenAndServe()
}

Go usa goroutines e channels para fan-out paralelo com controle de timeout via context.WithTimeout — o select coleta resultados à medida que chegam e retorna o que tiver ao expirar o timeout, garantindo degradação graciosa.

Decisões de engenharia

Edge Gateway centralizado vs BFF por cliente
Gateway centralizado é mais simples de operar: uma instância, um ponto de logging, uma configuração de autenticação. Adequado quando todos os clientes têm necessidades similares e a API é relativamente estável. BFF por tipo de cliente (mobile, web, parceiro) é a escolha certa quando as necessidades divergem significativamente: mobile precisa de payloads reduzidos e agregação de dados para economizar banda, web precisa de payloads ricos com paginação diferente, parceiros precisam de contrato mais estável com schema versionado. O custo do BFF é duplicação de lógica transversal (autenticação, logging, circuit breaking) — mitigue com bibliotecas compartilhadas ou um gateway base que os BFFs estendem.
Gateway de mercado vs gateway em código
Kong, APIM, e AWS API Gateway entregam rate limiting, autenticação, logging e portal do desenvolvedor prontos — sem escrever código. São a escolha certa para APIs públicas com múltiplos consumers externos, planos de pricing, e necessidade de portal. Gateway em código (YARP, FastAPI, net/http) é preferível quando a lógica de roteamento é complexa demais para configuração declarativa, quando o time precisa de testar o gateway com unit tests, quando a integração com sistemas internos (feature flags, auth customizado) é profunda, ou quando o budget não comporta licenças enterprise. O ponto de inflexão: se você está escrevendo mais Lua dentro do Kong do que YAML de configuração, é hora de migrar para código.
Validação JWT local vs Token Introspection
Validação local (verificar assinatura com chave pública JWKS cacheada) é O(1) e sem round-trip — necessária para gateways de alta throughput. A limitação é que tokens revogados continuam válidos até a expiração. Token introspection (chamar o Authorization Server para cada request) detecta revogação imediata, mas adiciona latência (~10-50ms) e carga no AS — impraticável com >1k req/s sem caching. A solução equilibrada: JWTs de curta duração (5-15 minutos) + lista de revogação em Redis apenas para tokens comprometidos + validação local em todos os requests. Introspection fica reservada para endpoints de alto risco (pagamentos, alteração de senha) onde revogação imediata é crítica.
API Gateway vs Service Mesh — escopos distintos
API Gateway gerencia tráfego Norte-Sul (externo → interno): autenticação de consumers, rate limiting por plano, transformação de payload, portal do desenvolvedor, monetização. Service Mesh gerencia tráfego Leste-Oeste (serviço → serviço): mTLS entre serviços, circuit breaking, retry, observabilidade de comunicação interna. São complementares, não substitutos. A confusão surge porque ambos oferecem circuit breaking e observabilidade — mas o escopo é diferente: gateway aplica políticas de negócio (consumer X tem quota Y), mesh aplica políticas de rede (serviço A não pode falar com serviço B). Em sistemas pequenos, um gateway com configuração de circuit breaker é suficiente; em plataformas com dezenas de serviços, gateway + mesh é o padrão correto.

Exercícios práticos

  1. Instale o Kong localmente via Docker e configure um serviço para o seu backend local. Adicione os plugins: jwt (validação de token), rate-limiting (100 req/min por consumer), correlation-id e prometheus. Use deck dump para exportar a configuração declarativa. Critério: curl sem token retorna 401 com {"message": "Unauthorized"}; request com token válido passa; a 101ª request no minuto retorna 429 com Retry-After header; deck sync restaura a configuração completa a partir do YAML sem erros.
  2. Implemente um BFF de agregação que combine dados de dois backends em uma única resposta, com tratamento de falha parcial: se um backend retornar 5xx, retorne os dados do que funcionou com partial: true. Critério: com ambos os backends funcionando, latência total ≤ max(latências individuais) + 10ms e partial: false; ao derrubar um backend, resposta em ≤ timeout_configurado com partial: true e dados do backend funcional; ambos os backends com 5xx retorna partial: true com listas vazias (não 500).
  3. Implemente os quatro algoritmos de rate limiting (fixed window, sliding window counter, token bucket, leaky bucket) em memória. Teste o boundary: envie 100 requests nos últimos 500ms de uma janela e 100 nos primeiros 500ms da próxima. Critério: fixed window permite burst de 200 na fronteira; sliding window bloqueia o burst após ~100 requests em qualquer janela de 1 minuto; token bucket permite burst até o capacity configurado antes de restringir; leaky bucket processa a taxa constante independente do padrão de chegada.
  4. Configure AWS API Gateway (ou Azure APIM) com duas usage plans: basic (100 req/min, 1k req/dia) e pro (1000 req/min, 100k req/dia). Crie uma API Key para cada plano. Critério: a key basic recebe 429 após 100 requests em 1 minuto enquanto a key pro continua funcionando; ambas recebem 429 após esgotarem a quota diária; headers X-RateLimit-Remaining são retornados em cada resposta com valor decrementando.
  5. Implemente versionamento de API no gateway: exponha /api/v1/orders (backend legado) e /api/v2/orders (backend novo) simultaneamente. Critério: todas as respostas de /api/v1/orders incluem headers Sunset: <data-futura> e Deprecation: true; logs estruturados em JSON incluem campos consumer_id e api_version em cada request, permitindo query de "consumers ainda em v1"; /api/v2/orders responde sem headers Sunset.

Perguntas de entrevista

Qual a diferença fundamental entre API Gateway e reverse proxy? Quando cada um é apropriado?

Um reverse proxy é agnóstico ao conteúdo — ele encaminha bytes HTTP baseado em URL e aplica políticas de rede (TLS, load balancing, compressão, health checks). Ele não sabe que POST /orders é uma operação de negócio, que requer o scope orders:write, ou que o consumer "Acme Corp" tem quota de 1000 req/dia.

Um API Gateway entende APIs como produto: sabe que cada rota tem um contrato (schema OpenAPI), que consumers têm planos diferentes com quotas distintas, que tokens JWT carregam scopes que determinam acesso por endpoint, e que a resposta deve excluir campos internos antes de chegar ao cliente externo. Ele é o ponto de controle de negócio da API.

Use reverse proxy quando: comunicação interna entre serviços sem semântica de produto, serving de assets estáticos com proxy simples, ou quando as funcionalidades de gateway seriam configuradas via código (YARP, FastAPI) em vez de produto. Use API Gateway quando: API pública ou semi-pública com consumers externos, necessidade de portal do desenvolvedor e self-service de credenciais, múltiplos planos de preços, ou necessidade de analytics de uso por endpoint e consumer.

Como implementar rate limiting correto em múltiplas instâncias do gateway sem estado local?

Rate limiting com estado local (em memória de cada instância) falha em escala horizontal: 3 instâncias com limite de 100 req/min permitem 300 req/min efetivos se o load balancer distribui uniformemente. A solução é estado centralizado.

O padrão mais comum usa Redis como contador atômico compartilhado. Para sliding window counter: a instância do gateway incrementa uma chave Redis com INCRBY atômico e TTL, e o valor retornado é o total atual de todas as instâncias. Para token bucket: a instância lê o estado do bucket (tokens, last_refill), calcula o refill baseado no tempo decorrido, e usa uma transação Redis (MULTI/EXEC ou Lua script) para atualizar atomicamente — evitando race condition onde duas instâncias leem o mesmo valor e ambas consomem o mesmo token.

Overhead de latência: cada request requer 1-2 round-trips ao Redis (~0.5-2ms em rede local). Para throughput muito alto (>10k req/s), o Redis pode se tornar gargalo. Mitigações: Redis cluster para escalar, ou "approximate rate limiting" — cada instância sincroniza contadores com Redis periodicamente (Kong's sync_rate) em vez de por request, aceitando que o limite pode ser ultrapassado ligeiramente entre sincronizações.

Explique o padrão BFF (Backend for Frontend) — quando os benefícios superam os custos?

O padrão BFF cria um servidor dedicado por tipo de cliente (mobile, web, parceiro) que agrega chamadas a microsserviços backend e entrega exatamente o que aquele cliente precisa — sem over-fetching (web pedindo campos que mobile não usa) ou under-fetching (mobile fazendo múltiplas chamadas para montar uma tela).

Os benefícios são: (1) cada equipe de frontend controla seu próprio BFF, iterando sem depender do time de backend para criar novos endpoints; (2) o BFF pode agregar, transformar e adaptar contratos sem modificar os serviços internos; (3) payloads otimizados por cliente (mobile recebe 3 campos, web recebe 15); (4) contrato estável para parceiros externos enquanto os serviços internos evoluem livremente.

Os custos são: duplicação de lógica transversal (autenticação, logging, circuit breaking) em cada BFF; mais serviços para operar e monitorar; possível inconsistência entre BFFs se a lógica de negócio for duplicada em vez de centralizada. Os benefícios superam os custos quando: há pelo menos 2 tipos de clientes com necessidades genuinamente diferentes, as equipes de frontend têm autonomia para deployar, e o volume de chamadas ao backend por tela justifica a agregação. Para sistemas com um único tipo de cliente e API estável, um gateway centralizado é mais simples.

Como o padrão de circuit breaker no gateway protege os backends e evita falha em cascata?

Sem circuit breaker, quando um backend fica lento, as requisições ao gateway se acumulam aguardando timeout — esgotando threads/goroutines do gateway, que começa a recusar requisições de outros backends também saudáveis (falha em cascata). O circuit breaker interrompe esse ciclo.

O circuit breaker tem três estados: Closed (operação normal, todas as requisições passam), Open (backend com falha detectada — requisições são rejeitadas imediatamente com 503 sem tentar o backend), Half-Open (após o timeout, permite um número limitado de requisições de teste — se passarem, fecha o circuito; se falharem, abre novamente).

No Envoy, o circuit breaker (outlier detection) é passivo: após N erros 5xx consecutivos de uma instância, ela é ejetada do pool por um tempo base exponencial — sem estado de "aberto/fechado" global, mas por instância individual. Isso é mais granular: se 1 de 3 instâncias está com problema, apenas ela é ejetada, não todo o cluster. Para circuit breaking no nível do cluster (todos com problema), o max_requests e max_pending_requests limitam a concorrência total e fazem o Envoy retornar 503 quando os limites são ultrapassados.

Retry só deve ser configurado com backoff exponencial e jitter para evitar thundering herd (todos retentando ao mesmo tempo ao voltar o backend), e apenas em operações idempotentes (GET, HEAD) ou em erros de conexão antes de enviar o request.

Como versionar APIs via gateway sem breaking change para consumers existentes?

O gateway permite manter múltiplas versões coexistindo com zero downtime para consumers. As estratégias de versionamento via URL (/v1/, /v2/) são as mais claras para consumers — cada versão é uma rota separada no gateway que aponta para o backend correto (ou para o mesmo backend com transformação de payload).

O ciclo de vida correto: (1) ao lançar v2, manter v1 operacional; (2) adicionar headers Sunset: <data> e Deprecation: true em todas as respostas de v1 — isso é padronizado no RFC 8594 e ferramentas como Insomnia e Postman alertam automaticamente; (3) monitorar consumers ainda em v1 via logs (agregar por consumer_id × api_version); (4) contatar consumers com uso significativo de v1 para oferecer suporte à migração; (5) na data de sunset, retornar 410 Gone com link para v2.

Estratégia alternativa para mudanças compatíveis: em vez de nova versão, adicionar campos opcionais ao schema existente (sem remover campos antigos). O gateway pode aplicar transformações de resposta para remover campos novos de clients que declaram versão antiga via header Accept-Version. Isso evita proliferação de versões para mudanças aditivas — reserve v2, v3 para mudanças breaking (remoção de campo, mudança de tipo, renomeação de rota).

Referências

  1. docs Kong Gateway Documentation — Kong Inc. docs.konghq.com — Documentação oficial do Kong OSS e Enterprise: plugins (jwt, rate-limiting, openid-connect, prometheus), Deck para configuração declarativa como código, KIC (Kubernetes Ingress Controller), e Admin API. Referência para o gateway open source mais adotado.
  2. docs Amazon API Gateway Developer Guide — AWS. docs.aws.amazon.com/apigateway — Referência completa de REST API e HTTP API: authorizers JWT e Lambda, usage plans com throttling, integração com Cognito, WAF e CloudWatch. Inclui comparativo REST API vs HTTP API (custo vs funcionalidades).
  3. docs Azure API Management Documentation — Microsoft. learn.microsoft.com/azure/api-management — Documentação do APIM: policies XML (inbound/backend/outbound), portal do desenvolvedor, analytics por subscription, integração com Azure AD e managed identity. Referência para environments enterprise com ecossistema Microsoft.
  4. docs YARP — Yet Another Reverse Proxy — Microsoft. microsoft.github.io/reverse-proxy — Documentação da biblioteca de reverse proxy para ASP.NET Core: roteamento programático, transforms de request/response, load balancing policies, active health checks e session affinity. A escolha para gateways em código C# com lógica complexa.
  5. livro Microservices Patterns — Chris Richardson (Manning, 2018). Capítulo 8 — "External API patterns": analisa API Gateway, BFF e comparação com Client-side composition. Inclui sequência de diagrama mostrando como o gateway agrega chamadas e os trade-offs de gateway centralizado vs BFF para mobile/web. Livro padrão da indústria para arquitetura de microsserviços.
  6. livro Building Microservices — Sam Newman (2ª ed., O'Reilly, 2021). Capítulo sobre "Communication Styles" e "Implementing Services" — discute gateways, BFF e evolução de APIs com versionamento. Newman cunhou o padrão BFF formalmente. Perspectiva prática de quem implementou microsserviços em escala.
  7. artigo Pattern: Backends for Frontends — Sam Newman (2015). samnewman.io/patterns/architectural/bff — Artigo original que formalizou o padrão BFF: motivação (mobile vs web com necessidades diferentes), estrutura (um BFF por tipo de cliente), e quando evitar (BFF como "catch-all" genérico que vira um monolito de camada intermediária).
  8. artigo Gateway Aggregation pattern — Microsoft Architecture Center. learn.microsoft.com/azure/architecture/patterns/gateway-aggregation — Descreve o padrão de agregação no gateway: fan-out paralelo, tratamento de falha parcial, e quando preferir agregação no gateway vs no cliente. Inclui considerações de latência e resiliência.
  9. standard RFC 6749 — The OAuth 2.0 Authorization Framework — IETF (2012). datatracker.ietf.org/doc/html/rfc6749 — Especificação do OAuth 2.0: grant types (Authorization Code, Client Credentials, Implicit, Resource Owner Password), token types, e fluxo de autorização. Base para entender como o API Gateway valida tokens e aplica scopes.
  10. standard RFC 8594 — The Sunset HTTP Header Field — IETF (2019). datatracker.ietf.org/doc/html/rfc8594 — Especificação do header Sunset para comunicar deprecação de endpoints com data de desativação. Referência para implementar ciclo de vida de versões de API no gateway de forma padronizada e interpretável por ferramentas.
  11. standard draft-ietf-httpapi-ratelimit-headers — IETF. datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers — Padronização em andamento dos headers RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset para APIs HTTP. Define semântica precisa de cada campo e como reportar múltiplas janelas simultaneamente.
  12. artigo An Introduction to API Gateways — Fielding, Roy T. — Architectural Styles and Design (2000). Dissertação UC Irvine — ics.uci.edu/~fielding/pubs/dissertation/top.htm — Dissertação original que definiu REST e Representational State Transfer. Base teórica para entender as restrições arquiteturais que os gateways implementam: interface uniforme, statelessness, e cacheability. Capítulo 5 é a leitura essencial.