@rudderstack/feature-flags
v0.2.0
Published
A comprehensive feature flag management system for the Rudder Integrations Library that supports multiple providers, environment-based overrides, and flexible configuration options.
Downloads
902
Readme
Feature Flags Module
A comprehensive feature flag management system for the Rudder Integrations Library that supports multiple providers, environment-based overrides, and flexible configuration options.
Table of Contents
- Overview
- Quick Start
- Configuration
- Usage Examples
- Providers
- Environment Variables
- Flag Registry
- Error Handling
- Advanced Features
- Implementation Design
- API Reference
Overview
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
Quick Start
Basic Setup
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}`);With Flagsmith Provider
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,
);Initialization Patterns
The Feature Flag Service supports three initialization patterns:
1. Global Service Instance (Recommended for most use cases)
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)
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)
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.
Configuration
Configuration Interface
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
}Default Values
| 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 |
Environment Variable Override
All configuration parameters can be overridden using environment variables:
# Provider configuration
FEATURE_FLAG_PROVIDER=flagsmith
FEATURE_FLAG_API_KEY=your-api-key
# Performance settings
FEATURE_FLAG_ENABLE_CACHE=true
FEATURE_FLAG_CACHE_TTL_SECONDS=300
FEATURE_FLAG_TIMEOUT_SECONDS=30
FEATURE_FLAG_RETRY_ATTEMPTS=5
# Feature settings
FEATURE_FLAG_ENABLE_LOCAL_EVALUATION=true
FEATURE_FLAG_ENABLE_ANALYTICS=falseUsage Examples
Basic Feature Flag Checking
// 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.Latest vs Cached Values
// 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}`);Error Handling Strategies
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);
}Working with Rich Responses
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
Local Provider
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:
await featureFlagService.initialize({
provider: 'local',
});Flagsmith Provider
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:
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
Global Configuration
Environment variables that affect the entire service:
# Provider selection
FEATURE_FLAG_PROVIDER=local|flagsmith
# Flagsmith API key
FEATURE_FLAG_API_KEY=your-api-key
# Performance settings
FEATURE_FLAG_ENABLE_CACHE=true
FEATURE_FLAG_CACHE_TTL_SECONDS=60
FEATURE_FLAG_TIMEOUT_SECONDS=60
FEATURE_FLAG_RETRY_ATTEMPTS=3
# Feature settings
FEATURE_FLAG_ENABLE_LOCAL_EVALUATION=false
FEATURE_FLAG_ENABLE_ANALYTICS=trueLocal Provider Flag Overrides
The Local provider supports runtime flag overrides through environment variables:
Global Flag Override
# Format: FEATURE_FLAG_LOCAL_{FLAG_NAME}
FEATURE_FLAG_LOCAL_NEW_DASHBOARD=true
FEATURE_FLAG_LOCAL_API_RATE_LIMIT=1000
FEATURE_FLAG_LOCAL_UI_THEME=darkWorkspace-Specific Override
# Format: FEATURE_FLAG_LOCAL__{WORKSPACE_ID}__{FLAG_NAME}
FEATURE_FLAG_LOCAL__workspace123__NEW_DASHBOARD=false
FEATURE_FLAG_LOCAL__workspace456__API_RATE_LIMIT=500Priority Order
- Workspace-specific environment variable (highest priority)
- Global environment variable
- Configured flag values
- Registry default values (lowest priority)
Example Usage
# Global setting
export FEATURE_FLAG_LOCAL_FEATURE_X=true
# Workspace-specific override
export FEATURE_FLAG_LOCAL__prod_workspace__FEATURE_X=false
export FEATURE_FLAG_LOCAL__dev_workspace__FEATURE_X=true
# Different workspaces will get different valuesValue Parsing
Environment variable values are automatically parsed:
- Booleans:
true,false,1,0(case-insensitive) - Numbers: Any valid numeric string
- Strings: Any other value
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
Registering Flags
The registry system allows you to define flag schemas and default values:
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);Using the Registry
// 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"Loading Flags from Configuration
import { FeatureFlagLoader } from '@rudderstack/feature-flags';
// From JSON string
const flagsFromJson = FeatureFlagLoader.fromJSON(`{
"flags": [
{
"key": "feature-x",
"name": "Feature X",
"description": "Enable feature X",
"defaultValue": true,
"type": "boolean"
}
]
}`);
// 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
Error Behaviors
The module supports three error handling strategies:
1. RETURN_DEFAULT (Silent Failure)
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 true2. RETURN_RICH_ERROR (Default)
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
try {
const result = await featureFlagService.isFeatureEnabled(
user,
'feature-flag',
ErrorBehaviour.THROW_ERROR,
);
} catch (error) {
console.error('Feature flag evaluation failed:', error);
}Common Error Scenarios
- Network timeouts (Flagsmith provider)
- Invalid API keys (Flagsmith provider)
- Malformed flag names
- Provider initialization failures
- Cache errors
Advanced Features
Custom Registry
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);Registry Operations
// 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();Workspace-Specific Configuration
// 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}`);Performance Optimization
// 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
Architecture Overview
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 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘Design Principles
1. Strategy Pattern
The module uses the Strategy pattern for provider selection, eliminating conditional logic in the service layer:
// 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:
export class FeatureFlagService {
constructor(registry?: IFeatureFlagRegistry) {
this.flagRegistry = registry || new FeatureFlagRegistry();
}
}3. Interface Segregation
Clear interfaces define contracts between layers:
IFeatureFlagService- Service contractIFeatureFlagProvider- Provider contractIFeatureFlagRegistry- 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:
// 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);Component Interactions
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 evaluationFlag 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 clientEnvironment 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 valueError Handling Strategy
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
Performance Considerations
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
Core Service
FeatureFlagService
class FeatureFlagService implements IFeatureFlagService {
constructor(registry?: IFeatureFlagRegistry);
// Factory method overloads: Create and initialize in one step
static create(
config?: Partial<FeatureFlagConfig>,
flags?: FeatureFlagDefinition[],
): Promise<FeatureFlagService>;
static create(
config?: Partial<FeatureFlagConfig>,
registry?: IFeatureFlagRegistry,
): Promise<FeatureFlagService>;
// Initialization
initialize(config?: Partial<FeatureFlagConfig>): Promise<void>;
// Flag evaluation (cached)
isFeatureEnabled(
user: FeatureFlagUser,
flagName: string,
onErrorBehaviour?: ErrorBehaviour,
): Promise<FeatureFlagResponse>;
getFeatureValue(
user: FeatureFlagUser,
flagName: string,
onErrorBehaviour?: ErrorBehaviour,
): Promise<FeatureFlagResponse>;
// Flag evaluation (latest)
isFeatureEnabledLatest(
user: FeatureFlagUser,
flagName: string,
onErrorBehaviour?: ErrorBehaviour,
): Promise<FeatureFlagResponse>;
getFeatureValueLatest(
user: FeatureFlagUser,
flagName: string,
onErrorBehaviour?: ErrorBehaviour,
): Promise<FeatureFlagResponse>;
// Registry management
registerFlags(flags: FeatureFlagDefinition[]): void;
getRegisteredFlag(key: string): FeatureFlagDefinition | undefined;
}Registry System
FeatureFlagRegistry
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
class FeatureFlagLoader {
static fromJSON(json: string): FeatureFlagDefinition[];
static fromObject(config: FeatureFlagConfig): FeatureFlagDefinition[];
static fromEnvironment(): FeatureFlagDefinition[];
}Type Definitions
Core Types
interface FeatureFlagUser {
workspaceId: string;
traits?: Map<TRAIT_KEYS, string>;
}
interface FeatureFlagResponse extends FeatureValue {
isDefault: boolean;
}
interface FeatureValue {
name: string;
enabled: boolean;
value?: unknown;
metadata?: Record<string, unknown>;
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',
}Environment Variables Reference
Service Configuration
FEATURE_FLAG_PROVIDER- Provider selection ('local' | 'flagsmith')FEATURE_FLAG_API_KEY- Flagsmith API keyFEATURE_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 overrideFEATURE_FLAG_LOCAL__{WORKSPACE_ID}__{FLAG_NAME}- Workspace-specific override
Configuration Loading
FEATURE_FLAGS_CONFIG_PATH- Path to JSON configuration file
Examples and Best Practices
Production Setup
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,
);Development Setup
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';Testing Setup
// 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.
