Skip to content

Reference

TypeScript, React and Angular SDK

Use @forgeops/fluxgate in Node.js and browsers, with React hooks or Angular signals on top, and keep every flag for a user in memory.

Before you start

  • Node.js 20 or newer. React 18 or newer, or Angular 19 or newer, for the adapters.
  • The edge server is running and express-checkout is deployed to Development. See Clients and edge server.
  • For Node.js, the SDK key of a Backend client: checkout-service. For React and Angular, the SDK key of a Web client whose Web Origins list your app's origin: juniper-web.

Packages

PackageUse it forLinks
@forgeops/fluxgateNode.js and browsers. FluxGateClient (retries, timeouts, cache), FlagSnapshot for synchronous reads with polling, and OpenFeature providers for the server and web SDKs. No dependencies.npm
@forgeops/fluxgate-reactReact 18 and 19. FluxGateProvider and the hooks useFlag, useFlagDetails, useIsEnabled, useFluxGateStatus.npm
@forgeops/fluxgate-angularAngular 19 and later, with or without zone.js. provideFluxGate(), FluxGateService with signals, and the *fgFlag directive.npm

Source code, samples and the full reference: forgeopslabs/fluxgate-js on GitHub. The current version is 1.0.2. Every package includes TypeScript types.

Node.js

Install
npm install @forgeops/fluxgate
checkout.ts
import { FluxGateClient } from '@forgeops/fluxgate';

const fluxgate = new FluxGateClient({
  baseUrl: 'http://localhost:8081',
  sdkKey: process.env.FLUXGATE_SDK_KEY!, // <clientId>.<apiKey>
});

const user = { targetingKey: 'user-1', country: 'CA', user_tier: 'plus' };

const variant = await fluxgate.getString('express-checkout', 'classic', user);
console.log(`checkout: ${variant}`);

const details = await fluxgate.evaluate('express-checkout', 'classic', user);
console.log(details);
Output
checkout: express
{
  key: 'express-checkout',
  reason: 'TARGETING_MATCH',
  value: 'express',
  variant: 'express'
}
MethodReturns
isEnabled(key, context)true only when a boolean flag is on
getBoolean, getString, getNumber, getObjectThe value, or your default
evaluate(key, default, context)FlagEvaluation: value, variant, reason, errorCode, errorMessage, metadata
evaluateAll(context, version?)Every flag in one call; throws FluxGateError on failure
watch(context, { pollIntervalMs })A FlagSnapshot with every flag for one context, kept current by polling
isHealthy()true when the edge answers GET /health with 2xx
OptionDefault
timeoutMs5000
retry.maxAttempts, retry.initialBackoffMs3, 100
cache.enabled, cache.ttlMs, cache.maxSizetrue, 30000, 1000
fetchThe global fetch

React

Install
npm install @forgeops/fluxgate @forgeops/fluxgate-react
App.tsx
import { FluxGateProvider, useFlag, useFluxGateStatus, useIsEnabled } from '@forgeops/fluxgate-react';

const options = {
  baseUrl: 'http://localhost:8081',
  sdkKey: import.meta.env.VITE_FLUXGATE_SDK_KEY, // the juniper-web SDK key
};

export function App({ user }: { user: { id: string; country: string; tier: string } }) {
  return (
    <FluxGateProvider options={options} context={{ targetingKey: user.id, country: user.country, user_tier: user.tier }}>
      <Checkout />
    </FluxGateProvider>
  );
}

function Checkout() {
  const { ready } = useFluxGateStatus();
  const checkout = useFlag('express-checkout', 'classic');
  const banner = useIsEnabled('holiday-banner');

  if (!ready) return <p>Loading…</p>;
  return (
    <>
      {banner && <HolidayBanner />}
      {checkout === 'express' ? <ExpressCheckout /> : <ClassicCheckout />}
    </>
  );
}

The provider loads every flag for the context in one bulk call and polls every 30 seconds (pollIntervalMs, 0 turns polling off). When the context changes by value, for example after sign-in, it loads the flags again. Hooks read from memory, and components re-render only when a flag or the status changes. The provider works in StrictMode.

HookReturns
useFlag(key, default)The value. The default is returned while loading and on errors. A string default gives a string; to type the variants, pass them: useFlag<'classic' | 'express'>(...).
useFlagDetails(key, default)The full FlagEvaluation.
useIsEnabled(key)true only when a boolean flag is on.
useFluxGateStatus(){ status, ready, error }. status is NOT_READY, READY, STALE or ERROR.
useFluxGateClient()The FluxGateClient, for evaluations on demand.

Angular

Install
npm install @forgeops/fluxgate @forgeops/fluxgate-angular
main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideFluxGate } from '@forgeops/fluxgate-angular';

bootstrapApplication(AppComponent, {
  providers: [
    provideFluxGate({
      options: { baseUrl: 'http://localhost:8081', sdkKey: environment.fluxgateSdkKey }, // the juniper-web SDK key
      context: { targetingKey: 'user-1', country: 'CA', user_tier: 'plus' },
    }),
  ],
});
checkout.component.ts
import { Component, inject } from '@angular/core';
import { FluxGateFlagDirective, FluxGateService } from '@forgeops/fluxgate-angular';

@Component({
  selector: 'app-checkout',
  imports: [FluxGateFlagDirective],
  template: `
    <app-holiday-banner *fgFlag="'holiday-banner'" />
    @if (checkout() === 'express') {
      <app-express-checkout />
    } @else {
      <app-classic-checkout />
    }
  `,
})
export class CheckoutComponent {
  private readonly fluxgate = inject(FluxGateService);
  readonly checkout = this.fluxgate.flag('express-checkout', 'classic');
}

By default, the application waits for the first load before it starts (waitForReady: true), so templates never show the defaults first. FluxGateService also offers details(key, default), isEnabled(key), the status, ready and error signals, and setContext(context) to switch users after sign-in.

OpenFeature

The core package includes OpenFeature providers. Install the OpenFeature SDK you need next to it:

server.ts
import { OpenFeature } from '@openfeature/server-sdk';
import { FluxGateServerProvider } from '@forgeops/fluxgate/openfeature/server';

await OpenFeature.setProviderAndWait(
  new FluxGateServerProvider({ baseUrl: 'http://localhost:8081', sdkKey: process.env.FLUXGATE_SDK_KEY! }),
);
const variant = await OpenFeature.getClient().getStringValue('express-checkout', 'classic', {
  targetingKey: 'user-1',
  country: 'CA',
  user_tier: 'plus',
});
main.ts
import { OpenFeature } from '@openfeature/web-sdk';
import { FluxGateWebProvider } from '@forgeops/fluxgate/openfeature/web';

await OpenFeature.setContext({ targetingKey: 'user-1', country: 'CA', user_tier: 'plus' });
await OpenFeature.setProviderAndWait(
  new FluxGateWebProvider({ baseUrl: 'http://localhost:8081', sdkKey: '<juniper-web SDK key>' }),
);
const variant = OpenFeature.getClient().getStringValue('express-checkout', 'classic');

The web provider loads every flag in one call, reloads when you call OpenFeature.setContext, and emits ConfigurationChanged with the changed keys and Stale while it polls. getObjectValue accepts a flag of any type.

Good to know

  • Evaluations never throw. errorCode is one of FLAG_NOT_FOUND, TYPE_MISMATCH, TARGETING_KEY_MISSING, INVALID_CONTEXT, PROVIDER_NOT_READY, PARSE_ERROR, UNAUTHORIZED and GENERAL.
  • The default sets the expected type. null accepts any type.
  • A FlagSnapshot (React, Angular, the web provider) polls with If-None-Match, so an unchanged flag set costs a 304 with no body. When a refresh fails, the snapshot keeps the last flags and reports STALE.
  • A browser bundle ships its SDK key to every visitor. Use a Web client there and never a Backend client. The edge serves a Web client only to the origins in its Web Origins, and answers other origins with 403.
  • In browsers, use the signed-in user's ID as the targeting key, or a random ID stored on the device for anonymous visitors, so a visitor keeps the same variant.
  • Date attributes in a context are sent as ISO 8601 strings.

Try the samples

The repository has a Node.js sample, a React app and an Angular app. samples/docker-compose.yml starts a separate FluxGate stack on ports 28000 (admin UI), 28080 (backend) and 28081 (edge), and samples/setup.sh creates a Backend client, a Web client, a boolean flag and a contextual flag. Follow samples/README.md.

Next

For the other languages, see Java and Spring Boot and Rust. The HTTP calls behind every SDK are in the Edge server API.