Files
LCEssentials/feature-control-bff

Feature Control BFF

BFF mínimo para expor flags ao app sem expor Atomenta-Token no cliente.

Endpoint

  • POST /feature-control/bootstrap
  • POST /feature-control/telemetry/exposure
  • GET /health

Variáveis de ambiente

  • PORT (default: 8787)
  • ATOMENTA_ORIGIN (default: https://atomenta.com.br)
  • FEATURE_CONTROL_MODULE_TOKEN (ou ATOMENTA_FEATURE_CONTROL_MODULE_ID) obrigatório
  • FEATURE_CONTROL_ENVIRONMENT (default: production)
  • FEATURE_CONTROL_CACHE_TTL_MS (default: 60000)
  • FEATURE_CONTROL_TIMEOUT_MS (default: 2000)
  • FEATURE_CONTROL_DEFAULTS_JSON (opcional, JSON com defaults de fallback)

Exemplo de FEATURE_CONTROL_DEFAULTS_JSON:

{
  "fc.checkout_v2": { "enabled": false, "variant": "off" },
  "fc.search_ranking_v3": { "enabled": true, "variant": "on", "payload": { "model": "v3" } }
}

Request (App -> BFF)

{
  "environment": "production",
  "keys": ["fc.checkout_v2", "fc.search_ranking_v3"],
  "context": {
    "subjectType": "customer",
    "subjectId": "cust_123",
    "storeId": "store_001",
    "platform": "ios",
    "appVersion": "2.3.1",
    "attributes": {
      "city": "Belo Horizonte",
      "tier": "gold"
    }
  }
}

Se o app enviar Authorization: Bearer <JWT>, o BFF tenta extrair subjectId do JWT (sub/customerId/id) quando context.subjectId não vier.

Response (BFF -> App)

{
  "ok": true,
  "source": "live",
  "configVersion": 7,
  "evaluatedAt": "2026-04-16T12:00:00.000Z",
  "flags": {
    "checkoutV2": true,
    "searchRankingV3": "off"
  },
  "raw": {
    "fc.checkout_v2": {
      "enabled": true,
      "variant": "on",
      "payload": null,
      "reason": "rollout"
    }
  }
}

Quando o upstream falha (ex.: 429, 500, timeout), o BFF devolve fallback com defaults estáticos.

Execução local

cd feature-control-bff
FEATURE_CONTROL_MODULE_TOKEN="550e8400-e29b-41d4-a716-44665544000b" npm start

Testes

cd feature-control-bff
npm test