Skip to content

A feature flag is a switch on a piece of behavior in your app that you control from Control Center (your admin dashboard at app.thebridge.dev) instead of from a deploy. Wrap something in a flag and you can:

  • Ship dark: merge and deploy a feature while it’s still off for everyone, then turn it on when it’s ready.
  • Roll out gradually: turn it on for 10% of users, watch, then ramp to 25%, 50%, 100%.
  • Target a segment (an audience defined by attribute rules): turn it on only for holders of a privilege, workspaces whose plan includes a feature, an internal group, or any attribute your app sends.
  • Kill it instantly: something’s wrong in production? Flip the flag off and it works as a kill switch. No rollback, no redeploy.

Every one of those is an action you take in Control Center. Flip a flag there and it reaches every connected service live, typically within seconds. No restart, no redeploy. That’s possible because of how flags evaluate:

Bridge Feature Flags evaluates locally. The SDK keeps your flag rules in memory, evaluates them against in-process context, and receives rule changes over the live channel (a persistent realtime connection the SDK maintains). A flag check is a synchronous O(1) lookup with no network call and no await, so it’s safe to call directly in a hot request path.

  • No network on read. BridgeFlagsService.flag(), @RequireFlag, and @Flag evaluate against an in-memory rule cache; there’s no request per flag check.
  • Live rule updates arrive over the live channel as flag.updated / flag.removed messages and update the rule cache in place. So flipping a flag, ramping a rollout, or hitting a kill switch is a Control Center action; your deployed code never changes.
  • Telemetry (which flags evaluated to what) is batched and reported in the background, off the request path.

The module loads your flag rules when your service starts (waiting at most 5 s before serving anyway). When the live channel drops, flags keep their last-known values, refresh every 30 s until it is back, and reload in full on reconnect, so your service keeps working through Bridge outages. A flag check never throws and never blocks a request; the worst case is a stale value or, for an unconfigured flag, your default.

BridgeFlagsModule.forRoot takes a runtimeMode:

  • 'channel' (default): a long-running NestJS service subscribes to your app’s live channel, authenticating with its API key, and receives rule changes as they happen. This is what you want for a normal always-on API. The SDK connects only when the Bridge deployment says it admits server SDKs (GET /realtime/config); until then, and whenever the socket is down, the rules refresh every pullCache.ttlMs (default 30 s) instead.
  • 'pull': for ephemeral runtimes that can’t hold a socket (cron jobs, serverless functions, webhook handlers, CLI scripts). The RealtimeClient is skipped entirely; reads still evaluate locally, and the rule set refreshes at most every pullCache.ttlMs (default 30 s), triggered by reads, instead of receiving push updates.
BridgeFlagsModule.forRoot({
  apiBaseUrl: 'https://api.thebridge.dev',
  apiKey: process.env.BRIDGE_API_KEY!,
  runtimeMode: 'pull', // ephemeral runtime, no live socket
});

The Nest module runs in backend mode by default. A server process isn’t “a user,” so the evaluator never invents an identity, and, critically, it refuses to bucket a rolled-out rule without an explicit identity. If a percentage rollout is active and you don’t pass an identity, the SDK returns the safe default with a warning rather than randomizing per call (which would make the same user flip-flop request to request). Pass identity per eval; see Send context from your backend.

BridgeFlagsModule is auth-free: an apiBaseUrl and apiKey are all the configuration flags need, no other Bridge module required. With BridgeAuthGuard verifying the user, rules on their privileges work with no wiring; add BridgeModule and rules on the workspace’s plan features work too (see Target by plan feature or privilege).

Next: Get started.