A comprehensive feature flag management system for the Rudder Integrations Library that supports multiple providers, environment-based overrides, and flexible configuration options.
npm install @rudderstack/feature-flagsA comprehensive feature flag management system for the Rudder Integrations Library that supports multiple providers, environment-based overrides, and flexible configuration options.
- Overview
- Quick Start
- Configuration
- Usage Examples
- Providers
- Environment Variables
- Flag Registry
- Error Handling
- Advanced Features
- Implementation Design
- API Reference
The Feature Flags module provides a unified interface for managing feature flags across different providers (Flagsmith, Local) with support for:
- Multiple Providers: Flagsmith for production, Local for development/testing
- Environment Overrides: Runtime configuration via environment variables
- Rich Responses: Detailed metadata including error states and staleness
- Flexible Error Handling: Configurable error behaviors
- Type Safety: Full TypeScript support with comprehensive interfaces
- Registry System: Centralized flag definition management
- Caching: Configurable caching with TTL support
``typescript
import {
featureFlagService,
FeatureFlagService,
FeatureFlagRegistry,
FeatureFlagUser,
TRAIT_KEYS,
} from '@rudderstack/feature-flags';
// Set up custom flags
const customFlags = [
{
key: 'my-feature-flag',
name: 'My Feature Flag',
description: 'Enable my awesome feature',
defaultValue: false,
type: 'boolean' as const,
},
];
// Option 1: Use global service instance
featureFlagService.registerFlags(customFlags);
await featureFlagService.initialize();
// Option 2: Create and initialize in one step (Factory Method)
const service = await FeatureFlagService.create({}, customFlags);
// Option 3: Manual creation and initialization
const manualRegistry = new FeatureFlagRegistry();
manualRegistry.register(customFlags);
const manualService = new FeatureFlagService(manualRegistry);
await manualService.initialize();
// Create user context
const user: FeatureFlagUser = {
workspaceId: 'my-workspace',
traits: new Map([
[TRAIT_KEYS.ORGANIZATION_ID, 'org-123'],
[TRAIT_KEYS.USER_ID, 'user-456'],
]),
};
// Check if feature is enabled (using any of the service instances)
const result = await featureFlagService.isFeatureEnabled(user, 'my-feature-flag');
console.log(Feature enabled: ${result.enabled});`
`typescript
import { featureFlagService, FeatureFlagService } from '@rudderstack/feature-flags';
// Set up custom flags for Flagsmith
const flagsmithFlags = [
{
key: 'premium-features',
name: 'Premium Features',
description: 'Enable premium features for users',
defaultValue: false,
type: 'boolean' as const,
},
{
key: 'api-rate-limit',
name: 'API Rate Limit',
description: 'API rate limit per minute',
defaultValue: 100,
type: 'number' as const,
},
];
// Option 1: Using global service
featureFlagService.registerFlags(flagsmithFlags);
await featureFlagService.initialize({
provider: 'flagsmith',
apiKey: 'your-flagsmith-api-key',
enableCache: true,
cacheTtlSeconds: 300,
});
// Option 2: Using factory method
const flagsmithService = await FeatureFlagService.create(
{
provider: 'flagsmith',
apiKey: 'your-flagsmith-api-key',
enableCache: true,
cacheTtlSeconds: 300,
},
flagsmithFlags,
);
`
The Feature Flag Service supports three initialization patterns:
#### 1. Global Service Instance (Recommended for most use cases)
`typescript
import { featureFlagService, FeatureFlagRegistry } from '@rudderstack/feature-flags';
// Optional: Set up custom registry before initialization
const customRegistry = new FeatureFlagRegistry();
customRegistry.register([
{
key: 'new-dashboard',
name: 'New Dashboard',
description: 'Enable new dashboard UI',
defaultValue: false,
type: 'boolean',
},
]);
// Register flags with global service
featureFlagService.registerFlags(customRegistry.getAll());
await featureFlagService.initialize({
provider: 'flagsmith',
apiKey: 'your-api-key',
});
`
#### 2. Factory Method (Convenient for creating configured instances)
`typescript
import { FeatureFlagService, FeatureFlagRegistry } from '@rudderstack/feature-flags';
// Option 2a: Pass flags directly (simple approach)
const customFlags = [
{
key: 'api-timeout',
name: 'API Timeout',
description: 'API timeout in milliseconds',
defaultValue: 5000,
type: 'number' as const,
},
];
const service = await FeatureFlagService.create(
{
provider: 'flagsmith',
apiKey: 'your-api-key',
},
customFlags,
);
// Option 2b: Pass custom registry (advanced approach)
const customRegistry = new FeatureFlagRegistry();
customRegistry.register(customFlags);
// Registry can load from multiple sources, apply transformations, etc.
const advancedService = await FeatureFlagService.create(
{
provider: 'flagsmith',
apiKey: 'your-api-key',
},
customRegistry,
);
`
#### 3. Manual Creation (Full control over registry and initialization)
`typescript
import { FeatureFlagService, FeatureFlagRegistry } from '@rudderstack/feature-flags';
const customRegistry = new FeatureFlagRegistry();
const service = new FeatureFlagService(customRegistry);
await service.initialize({
provider: 'flagsmith',
apiKey: 'your-api-key',
});
`
When to use each pattern:
- Global Service: For most applications where you need a single service instance. Use registerFlags() to add custom flags after creation.
- Factory Method:
- With flag definitions: When you want to pass flag definitions directly (simple approach)
- With registry: When you need advanced registry features like loading from multiple sources, transformations, or complex flag management
- Manual Creation: When you need fine-grained control over initialization, custom registries, or complex setup logic.
`typescript`
interface FeatureFlagConfig {
provider: 'flagsmith' | 'local';
apiKey?: string; // Required for Flagsmith
enableLocalEvaluation?: boolean; // Default: false
enableCache?: boolean; // Default: true
cacheTtlSeconds?: number; // Default: 60
timeoutSeconds?: number; // Default: 60
retryAttempts?: number; // Default: 3
enableAnalytics?: boolean; // Default: true
}
| Parameter | Default | Description |
| ----------------------- | --------- | ------------------------------------- |
| provider | 'local' | Feature flag provider |enableLocalEvaluation
| | false | Enable local evaluation for Flagsmith |enableCache
| | true | Enable response caching |cacheTtlSeconds
| | 60 | Cache time-to-live in seconds |timeoutSeconds
| | 60 | Request timeout in seconds |retryAttempts
| | 3 | Number of retry attempts |enableAnalytics
| | true | Enable analytics tracking |
All configuration parameters can be overridden using environment variables:
`bashProvider configuration
FEATURE_FLAG_PROVIDER=flagsmith
FEATURE_FLAG_API_KEY=your-api-key
Usage Examples
$3
`typescript
// Simple boolean check
const isEnabled = await featureFlagService.isFeatureEnabled(user, 'new-dashboard');
if (isEnabled.enabled) {
// Show new dashboard
}// Get feature value (string, number, or boolean)
const themeResult = await featureFlagService.getFeatureValue(user, 'ui-theme');
const theme = themeResult.value; // Could be 'dark', 'light', etc.
`$3
`typescript
// Get cached value (faster, may be stale)
const cachedResult = await featureFlagService.isFeatureEnabled(user, 'feature-x');// Get latest value (slower, always fresh)
const latestResult = await featureFlagService.isFeatureEnabledLatest(user, 'feature-x');
console.log(
Cached: ${cachedResult.enabled}, Latest: ${latestResult.enabled});
console.log(Is stale: ${cachedResult.isStale});
`$3
`typescript
import { ErrorBehaviour } from '@rudderstack/feature-flags';// Return default value on error (silent failure)
const result1 = await featureFlagService.isFeatureEnabled(
user,
'feature-flag',
ErrorBehaviour.RETURN_DEFAULT,
);
// Return rich error object (default behavior)
const result2 = await featureFlagService.isFeatureEnabled(
user,
'feature-flag',
ErrorBehaviour.RETURN_RICH_ERROR,
);
if (result2.error) {
console.error('Feature flag error:', result2.error.message);
}
// Throw error on failure
try {
const result3 = await featureFlagService.isFeatureEnabled(
user,
'feature-flag',
ErrorBehaviour.THROW_ERROR,
);
} catch (error) {
console.error('Feature flag failed:', error);
}
`$3
`typescript
const response = await featureFlagService.getFeatureValue(user, 'api-rate-limit');console.log({
name: response.name, // 'api-rate-limit'
enabled: response.enabled, // true/false
value: response.value, // The actual value
isDefault: response.isDefault, // Whether using default value
lastUpdatedAt: response.lastUpdatedAt, // When last updated
isStale: response.isStale, // Whether cached value is stale
error: response.error, // Any error that occurred
metadata: response.metadata, // Additional provider metadata
});
// Track analytics event (if supported by provider)
if (response.track) {
response.track();
}
`Providers
$3
The Local provider is ideal for development, testing, and scenarios where you want to manage flags through environment variables or configuration.
Features:
- Environment variable overrides
- Workspace-specific configuration
- Registry-based defaults
- No external dependencies
Configuration:
`typescript
await featureFlagService.initialize({
provider: 'local',
});
`$3
The Flagsmith provider integrates with Flagsmith's feature flag service for production use.
Features:
- Remote flag management
- Real-time updates
- Advanced targeting
- Analytics tracking
- Local evaluation support
Configuration:
`typescript
await featureFlagService.initialize({
provider: 'flagsmith',
apiKey: 'your-flagsmith-environment-key',
enableLocalEvaluation: true, // For one time fetching, and evaluating flags locally
enableAnalytics: true, // Track flag usage
enableCache: true, // Cache responses
cacheTtlSeconds: 300, // 5-minute cache
});
`Environment Variables
$3
Environment variables that affect the entire service:
`bash
Provider selection
FEATURE_FLAG_PROVIDER=local|flagsmithFlagsmith API key
FEATURE_FLAG_API_KEY=your-api-keyPerformance settings
FEATURE_FLAG_ENABLE_CACHE=true
FEATURE_FLAG_CACHE_TTL_SECONDS=60
FEATURE_FLAG_TIMEOUT_SECONDS=60
FEATURE_FLAG_RETRY_ATTEMPTS=3Feature settings
FEATURE_FLAG_ENABLE_LOCAL_EVALUATION=false
FEATURE_FLAG_ENABLE_ANALYTICS=true
`$3
The Local provider supports runtime flag overrides through environment variables:
#### Global Flag Override
`bash
Format: FEATURE_FLAG_LOCAL_{FLAG_NAME}
FEATURE_FLAG_LOCAL_NEW_DASHBOARD=true
FEATURE_FLAG_LOCAL_API_RATE_LIMIT=1000
FEATURE_FLAG_LOCAL_UI_THEME=dark
`#### Workspace-Specific Override
`bash
Format: FEATURE_FLAG_LOCAL__{WORKSPACE_ID}__{FLAG_NAME}
FEATURE_FLAG_LOCAL__workspace123__NEW_DASHBOARD=false
FEATURE_FLAG_LOCAL__workspace456__API_RATE_LIMIT=500
`#### Priority Order
1. Workspace-specific environment variable (highest priority)
2. Global environment variable
3. Configured flag values
4. Registry default values (lowest priority)
#### Example Usage
`bash
Global setting
export FEATURE_FLAG_LOCAL_FEATURE_X=trueWorkspace-specific override
export FEATURE_FLAG_LOCAL__prod_workspace__FEATURE_X=false
export FEATURE_FLAG_LOCAL__dev_workspace__FEATURE_X=trueDifferent workspaces will get different values
`$3
Environment variable values are automatically parsed:
- Booleans:
true, false, 1, 0 (case-insensitive)
- Numbers: Any valid numeric string
- Strings: Any other value`bash
FEATURE_FLAG_LOCAL_ENABLE_FEATURE=true # Boolean: true
FEATURE_FLAG_LOCAL_MAX_RETRIES=5 # Number: 5
FEATURE_FLAG_LOCAL_API_VERSION=v2 # String: "v2"
`Flag Registry
$3
The registry system allows you to define flag schemas and default values:
`typescript
import { FeatureFlagDefinition } from '@rudderstack/feature-flags';// Define flag definitions
const flags: FeatureFlagDefinition[] = [
{
key: 'new-dashboard',
name: 'New Dashboard',
description: 'Enable the redesigned dashboard interface',
defaultValue: false,
type: 'boolean',
category: 'ui',
tags: ['dashboard', 'ui-refresh'],
},
{
key: 'api-rate-limit',
name: 'API Rate Limit',
description: 'Maximum API requests per minute',
defaultValue: 1000,
type: 'number',
category: 'performance',
tags: ['api', 'rate-limiting'],
},
{
key: 'ui-theme',
name: 'UI Theme',
description: 'Default UI theme for the application',
defaultValue: 'light',
type: 'string',
category: 'ui',
tags: ['theme', 'appearance'],
},
];
// Register flags
featureFlagService.registerFlags(flags);
`$3
`typescript
// Check if flag is registered
const isRegistered = featureFlagService.getRegisteredFlag('new-dashboard');// Get flag definition
const flagDef = featureFlagService.getRegisteredFlag('api-rate-limit');
console.log(flagDef?.description); // "Maximum API requests per minute"
`$3
`typescript
import { FeatureFlagLoader } from '@rudderstack/feature-flags';// From JSON string
const flagsFromJson = FeatureFlagLoader.fromJSON(
{);// From configuration object
const flagsFromConfig = FeatureFlagLoader.fromObject({
flags: [
{
key: 'feature-y',
name: 'Feature Y',
description: 'Enable feature Y',
defaultValue: 'default-value',
type: 'string',
},
],
});
// From environment (reads FEATURE_FLAGS_CONFIG_PATH)
process.env.FEATURE_FLAGS_CONFIG_PATH = '/path/to/flags.json';
const flagsFromEnv = FeatureFlagLoader.fromEnvironment();
// Register loaded flags
featureFlagService.registerFlags(flagsFromJson);
`Error Handling
$3
The module supports three error handling strategies:
#### 1. RETURN_DEFAULT (Silent Failure)
`typescript
const result = await featureFlagService.isFeatureEnabled(
user,
'feature-flag',
ErrorBehaviour.RETURN_DEFAULT,
);
// result.enabled will be the default value
// result.error will be undefined
// result.isDefault will be true
`#### 2. RETURN_RICH_ERROR (Default)
`typescript
const result = await featureFlagService.isFeatureEnabled(
user,
'feature-flag',
ErrorBehaviour.RETURN_RICH_ERROR,
);if (result.error) {
console.error('Error details:', {
name: result.error.name,
message: result.error.message,
stack: result.error.stack,
});
// result.enabled will be the default value
// result.isDefault will be true
}
`#### 3. THROW_ERROR
`typescript
try {
const result = await featureFlagService.isFeatureEnabled(
user,
'feature-flag',
ErrorBehaviour.THROW_ERROR,
);
} catch (error) {
console.error('Feature flag evaluation failed:', error);
}
`$3
- Network timeouts (Flagsmith provider)
- Invalid API keys (Flagsmith provider)
- Malformed flag names
- Provider initialization failures
- Cache errors
Advanced Features
$3
`typescript
import { FeatureFlagRegistry, FeatureFlagService } from '@rudderstack/feature-flags';// Create custom registry
const customRegistry = new FeatureFlagRegistry();
customRegistry.register({
key: 'custom-flag',
name: 'Custom Flag',
description: 'A custom flag',
defaultValue: true,
type: 'boolean',
});
// Use custom registry with service
// Option 1: Manual creation
const customService = new FeatureFlagService(customRegistry);
await customService.initialize();
// Option 2a: Factory method with flag definitions (simple)
const factoryService = await FeatureFlagService.create({}, [
{
key: 'custom-flag',
name: 'Custom Flag',
description: 'A custom flag',
defaultValue: true,
type: 'boolean' as const,
},
]);
// Option 2b: Factory method with custom registry (advanced)
const advancedFactoryService = await FeatureFlagService.create({}, customRegistry);
`$3
`typescript
// Get all flags
const allFlags = customRegistry.getAll();// Get flags by category
const uiFlags = customRegistry.getByCategory('ui');
// Check if flag exists
const exists = customRegistry.isRegistered('my-flag');
// Get default value
const defaultValue = customRegistry.getDefaultValue('my-flag');
// Clear all flags
customRegistry.clear();
`$3
`typescript
// Different behavior per workspace
const prodUser: FeatureFlagUser = {
workspaceId: 'prod-workspace',
traits: new Map([[TRAIT_KEYS.ORGANIZATION_ID, 'prod-org']]),
};const devUser: FeatureFlagUser = {
workspaceId: 'dev-workspace',
traits: new Map([[TRAIT_KEYS.ORGANIZATION_ID, 'dev-org']]),
};
// Set environment variables for different workspaces
// FEATURE_FLAG_LOCAL__prod_workspace__NEW_FEATURE=false
// FEATURE_FLAG_LOCAL__dev_workspace__NEW_FEATURE=true
const prodResult = await featureFlagService.isFeatureEnabled(prodUser, 'new-feature');
const devResult = await featureFlagService.isFeatureEnabled(devUser, 'new-feature');
console.log(
Prod: ${prodResult.enabled}, Dev: ${devResult.enabled});
`$3
`typescript
// Configure for high-performance scenarios
await featureFlagService.initialize({
provider: 'flagsmith',
apiKey: 'your-key',
enableLocalEvaluation: true, // Reduce network calls
enableCache: true, // Cache responses
cacheTtlSeconds: 600, // 10-minute cache
timeoutSeconds: 5, // Fast timeout
retryAttempts: 1, // Minimal retries
});// Use cached methods for better performance
const cachedResult = await featureFlagService.isFeatureEnabled(user, 'flag');
// Use latest methods only when freshness is critical
const latestResult = await featureFlagService.isFeatureEnabledLatest(user, 'flag');
`Implementation Design
$3
The Feature Flags module follows a layered architecture with clear separation of concerns:
`
┌─────────────────────────────────────────────────────────────┐
│ Client Application │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ Service Layer │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ FeatureFlagService │ │
│ │ - Unified API │ │
│ │ - Error handling strategies │ │
│ │ - Response processing │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ Provider Layer │
│ ┌─────────────────┐ ┌─────────────────────┐ │
│ │ LocalProvider │ │ FlagsmithProvider │ │
│ │ - Env variables │ │ - Remote API │ │
│ │ - Registry │ │ - SDK integration │ │
│ │ - Workspace │ │ - Caching │ │
│ └─────────────────┘ └─────────────────────┘ │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ Infrastructure Layer │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ Registry │ │ Config │ │ Utils │ │
│ │ - Flag storage │ │ - Env parsing │ │ - Validation│ │
│ │ - Validation │ │ - Defaults │ │ - Helpers │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
`$3
#### 1. Strategy Pattern
The module uses the Strategy pattern for provider selection, eliminating conditional logic in the service layer:
`typescript
// Factory creates appropriate provider
const factory = new FeatureFlagProviderFactory(registry);
this.provider = factory.create(resolvedConfig);// Service uses provider polymorphically
const result = await this.provider.isFeatureEnabled(user, flagName);
`#### 2. Dependency Injection
Components accept dependencies through constructors, enabling testability and flexibility:
`typescript
export class FeatureFlagService {
constructor(registry?: IFeatureFlagRegistry) {
this.flagRegistry = registry || new FeatureFlagRegistry();
}
}
`#### 3. Interface Segregation
Clear interfaces define contracts between layers:
-
IFeatureFlagService - Service contract
- IFeatureFlagProvider - Provider contract
- IFeatureFlagRegistry - Registry contract#### 4. Single Responsibility
Each class has a focused responsibility:
- Service: API orchestration and response processing
- Providers: Flag evaluation logic
- Registry: Flag definition management
- Config: Environment variable resolution
- Utils: Validation and helper functions
#### 5. Open/Closed Principle
The system is open for extension (new providers) but closed for modification:
`typescript
// Adding a new provider requires no changes to existing code
export class CustomProvider implements IFeatureFlagProvider {
// Implementation
}// Register in factory
case 'custom':
return new CustomProvider(this.registry);
`$3
#### Initialization Flow
`
1. Client calls service.initialize(config)
2. ConfigResolver processes config + env variables
3. ProviderFactory creates appropriate provider
4. Provider initializes (SDK setup for Flagsmith)
5. Service ready for flag evaluation
`#### Flag Evaluation Flow
`
1. Client calls service.isFeatureEnabled(user, flag)
2. Service delegates to provider.isFeatureEnabled()
3. Provider evaluates flag:
- Local: Check env vars → registry → defaults
- Flagsmith: Query SDK → cache → network
4. Provider returns FeatureValue
5. Service processes result based on ErrorBehaviour
6. Service returns FeatureFlagResponse to client
`#### Environment Variable Resolution (Local Provider)
`
1. Check FEATURE_FLAG_LOCAL__{workspaceId}__{FLAG_NAME}
2. Check FEATURE_FLAG_LOCAL_{FLAG_NAME}
3. Check registry configured values
4. Return registry default value
`$3
The module implements a comprehensive error handling strategy:
#### Provider Level
- Providers catch and wrap errors in FeatureValue.error
- Network errors, timeouts, and API failures are handled gracefully
- Providers never throw exceptions during evaluation
#### Service Level
- Service processes provider errors based on ErrorBehaviour
- Three strategies: silent failure, rich error, or exception
- Default values are always available through registry
#### Client Level
- Clients can choose error handling strategy per call
- Rich responses provide full context for debugging
- Analytics tracking remains functional even during errors
$3
#### Caching Strategy
- Flagsmith: Built-in SDK caching with configurable TTL
- Local: No caching needed (environment variables are fast)
- Service: No additional caching layer to avoid complexity
#### Network Optimization
- Local Evaluation: Reduces network calls for Flagsmith
- Timeouts: Configurable to prevent hanging requests
- Retries: Configurable retry attempts with exponential backoff
#### Memory Management
- Registry: In-memory storage for flag definitions
- Providers: Stateless design for easy scaling
- SDK: Managed lifecycle with proper cleanup
API Reference
$3
#### FeatureFlagService
`typescript
class FeatureFlagService implements IFeatureFlagService {
constructor(registry?: IFeatureFlagRegistry); // Factory method overloads: Create and initialize in one step
static create(
config?: Partial,
flags?: FeatureFlagDefinition[],
): Promise;
static create(
config?: Partial,
registry?: IFeatureFlagRegistry,
): Promise;
// Initialization
initialize(config?: Partial): Promise;
// Flag evaluation (cached)
isFeatureEnabled(
user: FeatureFlagUser,
flagName: string,
onErrorBehaviour?: ErrorBehaviour,
): Promise;
getFeatureValue(
user: FeatureFlagUser,
flagName: string,
onErrorBehaviour?: ErrorBehaviour,
): Promise;
// Flag evaluation (latest)
isFeatureEnabledLatest(
user: FeatureFlagUser,
flagName: string,
onErrorBehaviour?: ErrorBehaviour,
): Promise;
getFeatureValueLatest(
user: FeatureFlagUser,
flagName: string,
onErrorBehaviour?: ErrorBehaviour,
): Promise;
// Registry management
registerFlags(flags: FeatureFlagDefinition[]): void;
getRegisteredFlag(key: string): FeatureFlagDefinition | undefined;
}
`$3
#### FeatureFlagRegistry
`typescript
class FeatureFlagRegistry implements IFeatureFlagRegistry {
// Registration
register(definition: FeatureFlagDefinition): void;
register(definitions: FeatureFlagDefinition[]): void; // Retrieval
get(key: string): FeatureFlagDefinition | undefined;
getAll(): FeatureFlagDefinition[];
getByCategory(category: string): FeatureFlagDefinition[];
getDefaultValue(key: string): boolean | string | number;
// Utilities
isRegistered(key: string): boolean;
clear(): void;
}
`#### FeatureFlagLoader
`typescript
class FeatureFlagLoader {
static fromJSON(json: string): FeatureFlagDefinition[];
static fromObject(config: FeatureFlagConfig): FeatureFlagDefinition[];
static fromEnvironment(): FeatureFlagDefinition[];
}
`$3
#### Core Types
`typescript
interface FeatureFlagUser {
workspaceId: string;
traits?: Map;
}interface FeatureFlagResponse extends FeatureValue {
isDefault: boolean;
}
interface FeatureValue {
name: string;
enabled: boolean;
value?: unknown;
metadata?: Record;
lastUpdatedAt?: Date;
isStale?: boolean;
error?: Error;
track?: () => void;
}
interface FeatureFlagDefinition {
key: string;
name: string;
description: string;
defaultValue: boolean | string | number;
type: 'boolean' | 'string' | 'number';
category?: string;
tags?: string[];
}
enum ErrorBehaviour {
RETURN_DEFAULT = 'return_default',
RETURN_RICH_ERROR = 'return_rich_error',
THROW_ERROR = 'throw_error',
}
`$3
#### Service Configuration
-
FEATURE_FLAG_PROVIDER - Provider selection ('local' | 'flagsmith')
- FEATURE_FLAG_API_KEY - Flagsmith API key
- FEATURE_FLAG_ENABLE_CACHE - Enable caching (boolean)
- FEATURE_FLAG_CACHE_TTL_SECONDS - Cache TTL (number)
- FEATURE_FLAG_TIMEOUT_SECONDS - Request timeout (number)
- FEATURE_FLAG_RETRY_ATTEMPTS - Retry attempts (number)
- FEATURE_FLAG_ENABLE_LOCAL_EVALUATION - Local evaluation (boolean)
- FEATURE_FLAG_ENABLE_ANALYTICS - Analytics tracking (boolean)#### Local Provider Flags
-
FEATURE_FLAG_LOCAL_{FLAG_NAME} - Global flag override
- FEATURE_FLAG_LOCAL__{WORKSPACE_ID}__{FLAG_NAME} - Workspace-specific override#### Configuration Loading
-
FEATURE_FLAGS_CONFIG_PATH - Path to JSON configuration file---
Examples and Best Practices
$3
`typescript
import {
featureFlagService,
FeatureFlagService,
FeatureFlagRegistry,
} from '@rudderstack/feature-flags';// Set up production flags
const productionFlags = [
{
key: 'new-checkout-flow',
name: 'New Checkout Flow',
description: 'Enable new checkout experience',
defaultValue: false,
type: 'boolean' as const,
},
{
key: 'max-cart-items',
name: 'Maximum Cart Items',
description: 'Maximum items allowed in cart',
defaultValue: 50,
type: 'number' as const,
},
];
// Option 1: Global service with production config
featureFlagService.registerFlags(productionFlags);
await featureFlagService.initialize({
provider: 'flagsmith',
apiKey: process.env.FLAGSMITH_API_KEY,
enableLocalEvaluation: true,
enableCache: true,
cacheTtlSeconds: 300,
timeoutSeconds: 10,
retryAttempts: 3,
enableAnalytics: true,
});
// Option 2a: Factory method with flags (simple)
const prodService = await FeatureFlagService.create(
{
provider: 'flagsmith',
apiKey: process.env.FLAGSMITH_API_KEY,
enableLocalEvaluation: true,
enableCache: true,
cacheTtlSeconds: 300,
timeoutSeconds: 10,
retryAttempts: 3,
enableAnalytics: true,
},
productionFlags,
);
// Option 2b: Factory method with registry (advanced)
const prodRegistry = new FeatureFlagRegistry();
prodRegistry.register(productionFlags);
// Registry can load additional flags from files, databases, etc.
const advancedProdService = await FeatureFlagService.create(
{
provider: 'flagsmith',
apiKey: process.env.FLAGSMITH_API_KEY,
enableLocalEvaluation: true,
enableCache: true,
cacheTtlSeconds: 300,
timeoutSeconds: 10,
retryAttempts: 3,
enableAnalytics: true,
},
prodRegistry,
);
`$3
`typescript
import { featureFlagService, FeatureFlagService } from '@rudderstack/feature-flags';// Set up development flags
const devFlags = [
{
key: 'debug-mode',
name: 'Debug Mode',
description: 'Enable debug logging and tools',
defaultValue: true,
type: 'boolean' as const,
},
{
key: 'mock-api-delay',
name: 'Mock API Delay',
description: 'Artificial delay for API calls in ms',
defaultValue: 0,
type: 'number' as const,
},
];
// Option 1: Global service for development
featureFlagService.registerFlags(devFlags);
await featureFlagService.initialize({
provider: 'local',
});
// Option 2: Factory method for development
const devService = await FeatureFlagService.create(
{
provider: 'local',
},
devFlags,
);
// Override flags via environment
process.env.FEATURE_FLAG_LOCAL_DEBUG_MODE = 'true';
process.env.FEATURE_FLAG_LOCAL_MOCK_API_DELAY = '500';
`$3
`typescript
// Test configuration with custom registry
const testRegistry = new FeatureFlagRegistry();
testRegistry.register([
{
key: 'test-feature',
name: 'Test Feature',
description: 'Feature for testing',
defaultValue: false,
type: 'boolean',
},
]);const testService = new FeatureFlagService(testRegistry);
await testService.initialize({ provider: 'local' });
``This comprehensive documentation covers all aspects of the Feature Flags module, from basic usage to advanced implementation details. The module provides a robust, flexible, and well-architected solution for feature flag management in the Rudder Integrations Library.