OpenFeature JavaScript SDKs

repository·main·Indexed 43 days ago

https://github.com/open-feature/js-sdk

Vendor-agnostic API for feature flagging in JavaScript. Includes specialized SDKs for Angular (supporting version 16+ with structural directives and FeatureFlagService) and NestJS (supporting version 8+ and Node.js 20+), allowing developers to integrate various feature flag providers without vendor lock-in.

Tokens
54.5K
Snippets
150
Records
201
Agent score
50%

What's inside OpenFeature

  1. What is OpenFeature and why use it?

    main
    OpenFeature is an open standard that provides a vendor-agnostic, community-driven API for feature flagging. It allows you to use your favorite feature flag management tools through a unified interface, which prevents vendor lock-in at the code level. By standardizing the interface, developers can switch providers without rewriting their application logic, and providers can focus on their unique features while remaining compatible with the standard.
  2. Best practices for library developers using OpenFeature

    main

    If you are developing a library that depends on OpenFeature components, you should list @openfeature/server-sdk as a peerDependency in your package.json.

    Why this is important:

    • Avoid Multiple Instances: It prevents the consumer from having multiple copies of the OpenFeature SDK, which can break singleton behavior.
    • Type Safety: It ensures that types remain consistent across the application.
    • Transitive Dependencies: You do not need to explicitly add @openfeature/core as a peer dependency, because @openfeature/server-sdk already handles that dependency transitively.
  3. Use OpenFeatureProvider context provider

    main

    The OpenFeatureProvider is a React context provider that represents a scope for feature flag evaluations. It binds an OpenFeature client to all evaluations within child components and enables the use of evaluation hooks.

    Note: OpenFeature.setProvider() should only be called once. You do not need to await its initialization; the React SDK handles re-rendering and suspense automatically.

    import {
      EvaluationContext,
      OpenFeatureProvider,
      useFlag,
      OpenFeature,
      TypedInMemoryProvider,
    } from '@openfeature/react-sdk';
    
    const flagConfig = {
      'new-message': {
        disabled: false,
        variants: {
          on: true,
          off: false,
        },
        defaultVariant: 'on',
        contextEvaluator: (context: EvaluationContext) => {
          if (context.silly) {
            return 'on';
          }
          return 'off';
        },
      },
    } as const;
    
    // Instantiate and set our provider (be sure this only happens once)!
    OpenFeature.setProvider(new TypedInMemoryProvider(flagConfig));
    
    // Enclose your content in the configured provider
    function App() {
      return (
        <OpenFeatureProvider>
          <Page></Page>
        </OpenFeatureProvider>
      );
    }
  4. Configure flag evaluation context injection

    main

    To trigger specific evaluation rules (e.g., based on user email or role), you can inject evaluation context into every flag evaluation for a request.

    Use the contextFactory option in OpenFeatureModule.forRoot() to define how context is built. This factory runs within a NestJS interceptor scope.

    By default, the interceptor is global. If you set useGlobalInterceptor: false in the module configuration, you can manually inject the contextFactory into route, module, or controller-bound interceptors.

  5. Scope OpenFeatureProvider using the domain parameter

    main

    By default, OpenFeatureProvider binds a client to all child components, but the underlying provider/context is controlled by the domain parameter. To ensure an OpenFeatureProvider is scoped to a specific provider or context, set the domain prop.

    <OpenFeatureProvider domain={'my-domain'>
      <Page />
    </OpenFeatureProvider>
  6. Choose the right OpenFeature SDK for your environment

    main

    The OpenFeature JavaScript SDKs are split into different packages depending on your runtime and framework. While they share many underlying types and components, their APIs may vary slightly to suit their specific environments.

    Server-side (Node.js and similar runtimes)

    • Server SDK: The core SDK for server-side environments.
    • NestJS SDK: A distribution of the Server SDK specifically optimized with built-in features for NestJS applications.

    Client-side (Web Browser)

    • Web SDK: The core SDK for use in web browsers.
    • React SDK: A distribution of the Web SDK specifically optimized with built-in features for React applications.
  7. Which OpenFeature packages to import in Angular

    main

    When working in an Angular project, you should import everything directly from @openfeature/angular-sdk.

    While @openfeature/angular-sdk re-exports everything from its peer dependencies (@openfeature/web-sdk and @openfeature/core) and adds Angular-specific features, you should avoid importing directly from @openfeature/web-sdk or @openfeature/core to ensure compatibility and proper integration with the Angular SDK.

  8. Implement Targeting with Evaluation Context

    main

    Targeting allows flag evaluation to consider dynamic criteria like user ID, location, or IP address. You provide this data using an evaluation context. The SDK merges context from three levels:

    1. Global Context: Set via OpenFeature.setContext().
    2. Client Context: Set via client.setContext().
    3. Invocation Context: Passed directly to the flag evaluation method (e.g., getBooleanValue).

    Context is merged by the SDK before evaluation occurs according to the OpenFeature specification.

    // Global context
    OpenFeature.setContext({ region: 'us-east-1' });
    
    // Client context
    const client = OpenFeature.getClient();
    client.setContext({ version: process.env.APP_VERSION });
    
    // Invocation context
    const requestContext = {
      targetingKey: req.session.id,
      email: req.session.email,
      product: req.productId,
    };
    
    const boolValue = await client.getBooleanValue('some-flag', false, requestContext);
  9. Use Domains to associate clients with specific providers

    main

    A domain is a logical identifier used to bind specific clients to specific providers. If a client requests a domain that has no associated provider, it will fall back to the default provider.

    • Registering a provider to a domain: OpenFeature.setProvider('my-domain', provider)
    • Getting a domain-scoped client: OpenFeature.getClient('my-domain')
    • Clearing context: Use OpenFeature.clearContext('my-domain') to clear a specific domain's context, or OpenFeature.clearContexts() to reset everything.
    import { OpenFeature, TypedInMemoryProvider } from '@openfeature/web-sdk';
    
    const myFlags = { /* ... flag definitions ... */ } as const;
    
    // Registering the default provider
    OpenFeature.setProvider(new TypedInMemoryProvider(myFlags));
    
    // Registering a provider to a specific domain
    OpenFeature.setProvider('my-domain', new TypedInMemoryProvider(someOtherFlags));
    
    // A Client bound to the default provider
    const clientWithDefault = OpenFeature.getClient();
    
    // A Client bound to the 'my-domain' provider
    const domainScopedClient = OpenFeature.getClient('my-domain');
  10. Use Multi-Provider to combine multiple flag sources

    main

    The MultiProvider allows you to manage multiple underlying providers as a single source of truth. This is useful for migrations (falling back from a new provider to an old one) or combining different data sources (e.g., environment variables + SaaS).

    By default, it uses the FirstMatchStrategy, which returns the first successful result and skips providers that return a FLAG_NOT_FOUND error.

    Evaluation Strategies

    • FirstMatchStrategy (default): Returns the first successful result. Skips providers returning FLAG_NOT_FOUND.
    • FirstSuccessfulStrategy: Returns the first successful result. Any error (not just FLAG_NOT_FOUND) causes the provider to be skipped.
    • ComparisonStrategy: Evaluates all providers. If all successful providers return the same value, that value is returned. Otherwise, it returns the value from a configured "fallback provider".
    import { MultiProvider, FirstSuccessfulStrategy } from '@openfeature/web-sdk';
    
    // Example: Using FirstSuccessfulStrategy
    const multiProvider = new MultiProvider(
      [{ provider: new ProviderA() }, { provider: new ProviderB() }],
      new FirstSuccessfulStrategy(),
    );
    
    await OpenFeature.setProviderAndWait(multiProvider);
    
    const client = OpenFeature.getClient();
    console.log(client.getBooleanDetails('my-flag', false));
  11. Handle tracking events with Multi-Provider

    main

    The MultiProvider supports tracking events across multiple providers. When you call client.track(...), the event is sent to all underlying providers that implement the track method.

    Default Behavior:

    • All providers receive tracking calls.
    • If one provider fails to track, others continue and errors are logged.
    • Providers in NOT_READY or FATAL status are automatically skipped.
    • Providers without a track method are gracefully skipped.
    import { OpenFeature } from '@openfeature/server-sdk';
    import { MultiProvider } from '@openfeature/server-sdk';
    
    const multiProvider = new MultiProvider([{ provider: new ProviderA() }, { provider: new ProviderB() }]);
    await OpenFeature.setProviderAndWait(multiProvider);
    
    const client = OpenFeature.getClient();
    
    // Tracked events are sent to all providers by default
    client.track('purchase', { targetingKey: 'user123' }, { value: 99.99, currency: 'USD' });