Skip to content

Ability to mark some feture flags as "sticky", meaning once evaluated for a user, the result will not change regarless of config changes to the flag #248

@besLisbeth

Description

@besLisbeth

Describe the feature request

I would like to propose a new configuration option for the unleash-proxy-client-js library that allows clients to filter or transform the list of feature toggles before the internal state is updated. This would provide a clean, flexible way to manage which toggles are updated, without overriding core logic such as the fetch method.

Background

In the current implementation, when the Unleash proxy client fetches toggles, it updates the internal state with the entire list of toggles returned by the proxy. This behavior works well for most scenarios, but introduces limitations for more complex use cases where only a subset of toggles should be processed. In our project, we want to update the list of feature flags with not very complex logic to not break the client and not to over-complicate the logic that could result in bad UX.

To work around this, I’m currently overriding the fetch method entirely, just to inject toggle transformation logic. However, this approach has significant downsides:

  • Hard to maintain: I must reimplement logic that the library already handles correctly.
  • Error-prone: Custom fetch logic risks breaking behavior like headers, caching, or error handling.
  • Tightly coupled: If internal fetch logic changes in future versions, the override could break silently.

My actual goal is not to replace fetch, but simply to intercept or shape the list of toggles before they’re applied to the client.

Solution suggestions

I propose introducing a new optional hook in the client configuration that allows consumers to control how the fetched toggles are processed before updating internal state.

Three possible designs:

Option A: Filtering hook

filterToggles?: (toggle: IToggle) => boolean;

Usage:
filterToggles: (toggle) => toggle.name.startsWith('app:feature:');

Option B: Transformation hook

transformToggles?: (toggles: IToggle[]) => IToggle[];

Usage:
transformToggles: (toggles) => toggles.filter(t => t.name.includes('beta'));

Option C: Asynchronous transformation hook

transformToggles?: (toggles: IToggle[]) => Promise<IToggle[]>;

Usage:

transformToggles: async (toggles) => {
  сonst isOk = await userConfirmation(); 
  return isOk ? toggles.filter(toggle => toggles.filter(t => t.name.includes('beta:for:best:users'))) : toggles;
}

These hooks would execute after the toggle list is fetched but before the client updates its internal feature map.

I prefer Option C as it's more flexible compared with the previous ones. It also opens the door to dynamic filtering or enrichment, such as:

  • Pulling context from APIs or storage;
  • Applying user- or environment-specific logic;
  • Delaying the toggle application until external criteria are met.

Benefits to have this feature overall:

  1. Clean and idiomatic: Keeps the library usage declarative
  2. Maintains encapsulation: No need to override core behavior like fetch
  3. More flexible: Allows both filtering and advanced transformation logic
  4. Safer: Leverages existing fetch implementation without duplicating logic

This addition would significantly improve flexibility while keeping the existing API clean and backward-compatible.
I would also like to write the PR to add this functionality to the code.

What do you think?

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

Status

For later

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions