Feature Flag Guard
This page describes published 0.5.0. Complete installation and the first HTTP request before adding more routes.
The @FeatureFlag() decorator automatically applies UseGuards(FeatureFlagGuard), so you do not need to add @UseGuards() yourself.
Method-level
import { FeatureFlag } from '@nestarc/feature-flag';
@Controller('dashboard')
export class DashboardController {
@FeatureFlag('NEW_DASHBOARD')
@Get()
getDashboard() {
return { message: 'Welcome to the new dashboard' };
}
}Class-level
@FeatureFlag('BETA_API')
@Controller('beta')
export class BetaController {
@Get('feature-a')
featureA() { /* guarded */ }
@Get('feature-b')
featureB() { /* guarded */ }
}Custom status code and fallback
@FeatureFlag('PREMIUM_FEATURE', {
statusCode: 402,
fallback: { message: 'Upgrade required' },
})
@Get('premium')
getPremiumContent() { ... }When the flag is disabled, the guard responds with the given statusCode (default 403) and optional fallback body.
Bypassing the guard
Use @BypassFeatureFlag() on methods that should always be accessible, even when a class-level flag is applied:
import { BypassFeatureFlag } from '@nestarc/feature-flag';
@FeatureFlag('BETA_API')
@Controller('beta')
export class BetaController {
@Get('docs')
betaDocs() { /* guarded by BETA_API */ }
@BypassFeatureFlag()
@Get('health')
healthCheck() {
return { status: 'ok' };
}
}Programmatic Evaluation
Inject FeatureFlagService for service-layer checks outside the HTTP request cycle:
import { FeatureFlagService } from '@nestarc/feature-flag';
@Injectable()
export class PaymentService {
constructor(private readonly flags: FeatureFlagService) {}
async processPayment(order: Order) {
const useNewGateway = await this.flags.isEnabled('NEW_PAYMENT_GATEWAY');
if (useNewGateway) {
return this.newGateway.process(order);
}
return this.legacyGateway.process(order);
}
}Evaluate all flags at once
const allFlags = await this.flags.evaluateAll({ userId: 'user-123' });
// { NEW_DASHBOARD: true, PREMIUM_FEATURE: false, ... }evaluateAll() returns active stored flags only. It does not evaluate registry-only keys, emit evaluation/exposure events, or convert a database/cache error to per-flag defaults. Published 0.5.0 also omits registry bucketBy in this path; see version limits.
Explicit evaluation context
Both isEnabled() and evaluateAll() accept an optional EvaluationContext to override the auto-detected context:
const enabled = await this.flags.isEnabled('MY_FLAG', {
userId: 'user-123',
tenantId: 'tenant-abc',
environment: 'staging',
});Passing null explicitly suppresses the ambient value for that dimension. The resolved attribute remains null and can still match an override containing null:
// Evaluate as if no user is present, even within a request with x-user-id
const result = await this.flags.isEnabled('MY_FLAG', { userId: null });