# Add Feature Flags

Add feature flags and A/B testing to your Terreno app using `@terreno/feature-flags`.

## Prerequisites

- A working Terreno backend using `TerrenoApp`
- `@terreno/admin-backend` registered for the admin panel (optional but recommended)

## Steps

### 1. Install the package

```bash
bun add @terreno/feature-flags
```

### 2. Register the backend plugin

In your server file, register `FeatureFlagsApp` and add the admin config:

```typescript
import {TerrenoApp} from "@terreno/api";
import {AdminApp} from "@terreno/admin-backend";
import {FeatureFlagsApp, featureFlagAdminConfig} from "@terreno/feature-flags";

// Define segment functions to classify users for targeting rules
const segments = {
  "pro-users": (user) => user.plan === "pro",
  "beta-testers": (user) => user.betaTester === true,
  "internal-team": (user) => user.email?.endsWith("@mycompany.com"),
};

new TerrenoApp({userModel: User})
  .register(new FeatureFlagsApp({segments}))
  .register(
    new AdminApp({
      models: [
        featureFlagAdminConfig,  // adds Feature Flags to the admin panel
        // ...your other models
      ],
    })
  )
  .start();
```

`featureFlagAdminConfig` is a pre-configured admin model config exported by the package. It sets up the display name, list fields, route path, and model reference so you don't need to configure them manually.

### 3. Regenerate the frontend SDK

After adding the backend routes, regenerate the SDK so the frontend can call the new endpoints (with the example backend running, or set `OPENAPI_URL` to your deployed `openapi.json`):

```bash
cd your-frontend && bun run sdk
```

Regeneration may emit a hook for `/feature-flags/flagConfiguration`; prefer **`useTerrenoFeatureFlags`** / **`useFeatureFlags`** from `@terreno/rtk` so OpenFeature domain, cache keying, and optional socket refetch stay consistent.

### 4. Use feature flags in the frontend

**Recommended (OpenFeature):** add `@openfeature/react-sdk` to the app, wrap the tree (or the subtree that reads flags) in `<OpenFeatureProvider domain="feature-flags">`, and call `useTerrenoFeatureFlags(terrenoApi, {userId, socket})` once after auth so the OpenFeature client receives `GET /feature-flags/flagConfiguration`. Then use standard hooks such as `useBooleanFlagValue` / `useStringFlagValue` / `FeatureFlag`.

**Back-compat:** `useFeatureFlags(terrenoApi)` from `@terreno/rtk` still returns `{flags, getFlag, getVariant, isLoading, …}` with the same semantics as before; it uses `/flagConfiguration` under the hood. You do not need to call a generated RTK hook for `/flagConfiguration` directly.

```typescript
import {useBooleanFlagValue} from "@openfeature/react-sdk";
import {useTerrenoFeatureFlags, useSelectCurrentUserId} from "@terreno/rtk";
import {terrenoApi} from "@/store/sdk";

const GatedBanner: React.FC = () => {
  const userId = useSelectCurrentUserId();
  useTerrenoFeatureFlags(terrenoApi, {userId, skip: !userId});
  const showBanner = useBooleanFlagValue("new-ui", false);
  return showBanner ? <NewUI /> : null;
};
```

```typescript
import {useFeatureFlags} from "@terreno/rtk";
import {terrenoApi} from "@/store/sdk";

const LegacyStyle: React.FC = () => {
  const {getFlag, isLoading} = useFeatureFlags(terrenoApi);

  if (isLoading) {
    return <Spinner />;
  }

  const showNewUI = getFlag("new-ui");

  return showNewUI ? <NewUI /> : <OldUI />;
};
```

### 4b. Live updates (optional)

To push flag changes to open clients without a refresh, pass **`liveUpdates`** when constructing `FeatureFlagsApp` (see `example-backend/src/server.ts`): Mongoose **`FeatureFlag.watch()`** drives a Socket.io broadcast. **MongoDB must run as a replica set** (a single-node replica set is enough); standalone instances cannot use change streams, and the plugin logs a warning if `watch()` fails.

On the client, pass the same Socket.io **client** into `useTerrenoFeatureFlags` / `useFeatureFlags` as `socket`; the hook listens for `featureFlagsChanged` (or `socketEventName`) and refetches `/flagConfiguration`. The event payload is only `{ key: string }` — the same flag keys are already returned to every authenticated user in `/flagConfiguration`, so this is not considered a new exposure.

### 5. Create flags via the admin panel

Navigate to the admin panel in your app. The "Feature Flags" card appears automatically. Create a flag with:

- **Key**: unique identifier, e.g. `new-ui`
- **Name**: human-readable label
- **Type**: `boolean` for on/off toggles, `variant` for A/B tests
- **Enabled**: global kill switch
- **Rules**: optional targeting (field-based or segment-based)
- **Rollout Percentage**: gradual rollout for boolean flags (default 100%)

## Next steps

- See the [reference docs](../reference/feature-flags.md) for the full API, targeting rules, and variant flags
- See `example-backend/src/server.ts` for a complete working example
