Files
LCEssentials/feature-control-bff/README.md
Daniel Arantes Loverde e62bd60e9a docs(feature-control): fix flag key prefix fc. → at. across all docs
Real key pattern is at.<domain>.<name> (e.g. at.ios.only, at.promo).
The fc. prefix in the BFF README and API_Mobile_App.md was incorrect.

- API_Mobile_App.md: updated request keys, response raw examples,
  flag table, and Swift usage examples to at. prefix
- API_Mobile_App.md: clarified that app uses raw["at.key"] not
  simplified flags object (FeatureFlagsState.isEnabled reads raw)
- feature-control-bff/README.md: updated DEFAULTS_JSON example,
  request keys, response flags/raw examples, added note about raw usage
2026-06-05 16:30:57 -03:00

2.7 KiB

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

Notificações

No momento este BFF cobre apenas Feature Control (bootstrap + exposure). Para notificações segmentadas por audiência (todos, feature:fc.ios), precisamos adicionar o contrato de endpoint de notificações do Atomenta para integrar no app.

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:

{
  "at.ios.only":     { "enabled": true,  "variant": "on"  },
  "at.android.only": { "enabled": true,  "variant": "on"  },
  "at.promo":        { "enabled": true,  "variant": "on"  },
  "at.city.aguai":   { "enabled": true,  "variant": "on"  }
}

Request (App -> BFF)

{
  "environment": "production",
  "keys": ["at.ios.only", "at.android.only", "at.promo", "at.city.aguai"],
  "context": {
    "subjectType": "customer",
    "subjectId": "cust_123",
    "storeId": "store_001",
    "platform": "ios",
    "appVersion": "2.3.1",
    "attributes": {
      "city": "Aguaí"
    }
  }
}

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": {
    "atiosonly":     true,
    "atandroidonly": false,
    "atpromo":       true,
    "atcityaguai":   true
  },
  "raw": {
    "at.ios.only": {
      "enabled": true,
      "variant": "on",
      "payload": null,
      "reason": "rollout"
    },
    "at.promo": {
      "enabled": true,
      "variant": "on",
      "payload": null,
      "reason": "rollout"
    }
  }
}

O app Swift usa o campo raw indexado pela chave original (ex: raw["at.promo"]). O campo flags é uma versão simplificada gerada mecanicamente (pontos removidos).


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

## Execução local

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

Testes

cd feature-control-bff
npm test

Autor: Daniel Arantes Loverde