Skip to content

This is one of the biggest advantages of building flags on Bridge instead of in isolation: Bridge already knows who’s signed in, what they are allowed to do, and what their workspace (called a tenant in the API) is paying for. A rule on any of those gives the same answer in your NestJS backend as in the browser, with no code from you. BridgeFlagGuard, @Flag(...) and BridgeContextInterceptor fill the attributes in on every request that BridgeAuthGuard verified.

Feature flags are the standard way to control a route, an API endpoint or a feature. Use one rule, written once in Control Center (your admin dashboard at app.thebridge.dev), and both sides follow it.

From the signed-in user’s token, verified by BridgeAuthGuard:

AttributeComes fromExample values
user.idthe token’s subthe signed-in user’s id
user.emailthe token’s emailjane@acme.com
tenant.idthe token’s tidthe current workspace’s id
privilegesthe token’s privilegesthe user’s privilege list, e.g. USER_READ

From the workspace’s billing, read from Bridge and cached per workspace:

AttributeExample values
bridge:billing.subscription.statuse.g. active, trial
bridge:billing.trialtrue while the workspace is on a trial
bridge:billing.entitlement.<feature>true when the plan includes the feature

Auth attributes aren’t prefixed and billing attributes are under bridge:billing., exactly as in the browser SDKs. Both sides use the same mapping from @nebulr-group/bridge-auth-core, so a name that works in one works in the other.

bridge:billing.quota.* (usage against a plan limit) is available in the browser but not yet on the server: the snapshot a server reads doesn’t carry quotas. Use plan limits for numbers on the backend; flags are the wrong tool for them anyway.

  • A feature a plan sells → bridge:billing.entitlement.<feature>. List the feature on the plans that sell it (bridge plan feature add <plan> <feature>), then rule the flag bridge:billing.entitlement.<feature> eq true. The plan is where the customer sees what they are buying, so changing what Pro includes stays one edit in one place, and the rule never names a plan. See Lock features to a plan.
  • What someone may do → a privilege rule (privileges contains "REPORTS_BETA"). contains is exact membership. Which roles hold a privilege is only true in the default setup and differs per app, so read the app’s real roles and privileges (list_roles / bridge role list) before writing the rule. A rule on the role itself (user.role eq "ADMIN") only when you mean the role, not what it can do.

Rule: on for users matching bridge:billing.entitlement.export eq true.

import { Controller, Get, UseGuards } from '@nestjs/common';
import { BridgeAuthGuard } from '@nebulr-group/bridge-nestjs';
import { RequireFlag, BridgeFlagGuard } from '@nebulr-group/bridge-nestjs/flags';

@Controller('reports')
@UseGuards(BridgeAuthGuard, BridgeFlagGuard) // auth first, so the token is verified
export class ReportsController {
  @Get('export')
  @RequireFlag('export_reports')
  exportReports() { /* … */ }
}

Rule: on for users matching privileges contains "BILLING_SETTINGS". Same controller shape, @RequireFlag('billing_settings') on the route. Nothing else changes.

BridgeContextInterceptor puts the resolved context on the request as req.bridgeFlagsContext; pass it to flag():

@Get('home')
home(@Req() req) {
  const showNew = this.flags.flag('new_home', false, req.bridgeFlagsContext);
  // …
}

Outside the interceptor, resolvedFlagContext(req, bridgeService) (from @nebulr-group/bridge-nestjs/flags) builds the same context. A bare this.flags.flag(key, default, { identity }) sees only the identity you pass.

The billing attributes come from the same workspace snapshot the plan-limit and entitlement checks read, cached for 30 seconds and shared by every user of the workspace. The cache is dropped early when a user of the workspace shows up with a newer token, which Bridge issues when their plan, role or privileges change. If Bridge can’t be reached, rules on bridge:billing.* see no value until it answers; rules on the token’s attributes keep working.

With BridgeFlagsModule on its own (no BridgeModule), the token attributes are still filled in; the billing attributes need BridgeModule.

Every attribute above comes from the token BridgeAuthGuard verified, or from Bridge itself. Nothing a client sends is used: not the x-bridge-context header, not a privilege or plan on req.user, not a bridgeFlagsContext property a middleware set. Your own per-call attributes still win on a key collision, so don’t pass client input there either.

The main @nebulr-group/bridge-nestjs entry point also ships @RequireFeatureFlag / FeatureFlagService, which asks Bridge’s API to evaluate each flag from the caller’s access token. Bridge resolves the same attributes there: privileges and the other token attributes, plus the workspace’s subscription status, trial and bridge:billing.entitlement.<feature> (not the plan-limit numbers, bridge:billing.quota.*). Both paths follow the same rule. See Gate features with flags.