tools.featureFlags contract while PostHog, LaunchDarkly, or a local dev stub handle evaluation behind the scenes.
Why
Feature flags belong in infrastructure, not scatteredif (process.env…) checks. Refract normalizes every provider into the same evaluation model (enabled + optional variant), adds request-level consistency for GraphQL, and optionally shares results across requests via cacheMs.
Public API
preload() is transparent — isEnabled, getVariant, and getAll call it automatically. Multiple reads in the same HTTP request hit the provider at most once when bulk caching succeeds.
Configuration
cacheMs: 0disables the shared cache; request caching remains enabled.exposedFlagscontrols which keys appear onme.featureFlags.
Provider contract
Remote packages implementFeatureFlagProviderType: the same surface as FeatureFlagTool except preload (the backend cache wrapper owns preload).
LOCAL returns a full
FeatureFlagTool directly (no cache wrapper). PostHog and LaunchDarkly return a provider; buildCachedFeatureFlagTool in the loader adds preload plus L1/L2 caching.
When adding a provider, implement all three evaluation methods. Bulk-first vendors should make getAll efficient; per-flag-only vendors can no-op getAll to {} and rely on fall-through (see below).
Caching
Resolution order per evaluation context key (user:123|org:456, anonymous, etc.):
Bulk path (preload / getAll)
- Request cache (always on for HTTP traffic via AsyncLocalStorage)
- Shared Redis cache (
tools.cache.helpers.featureFlagEvaluation, TTL fromcacheMs) — accessed through theFeatureFlagSharedCacheport - Provider
getAll(vendor bulk API)
Per-flag path (isEnabled / getVariant)
- Same request + shared bulk snapshot when the key is present in the cached map (including
enabled: false) - Fall-through to provider
isEnabled/getVariantwhen:- the key is absent from the bulk snapshot (e.g. provider returned
{}, or bulk omitted that flag) - bulk population failed (errors are swallowed; per-flag calls still work)
- the key is absent from the bulk snapshot (e.g. provider returned
{ enabled: false } is authoritative.
The L2 helper is registered in apps/backend/src/tools/cache/featureFlagEvaluation.ts and wired when buildFeatureFlags runs with tools.cache.
GraphQL
Authenticated clients can read frontend-safe flags fromme.featureFlags. Internal flags stay server-side and are accessed only through tools.featureFlags.
Evaluation context is built from the authenticated user (userId, current organization from membership). If user.memberships is not hydrated, the utility loads them from the database before resolving flags.
