Feature Toggles
Overview
[!IMPORTANT] Required Role: Super Admin only.
The feature_toggles module allows administrators to enable or disable features, modules, or specific functionality at runtime without code deployment. It also supports runtime configuration, allowing you to change application behavior (strings, numbers, JSON config) dynamically. This provides operational flexibility for controlled rollouts, client-specific customizations, and emergency kill switches.
Key Capabilities
- Hierarchical Resolution: Resolves flags from specific tenant overrides up to global defaults.
- High Performance: Uses aggressive caching with instant invalidation to minimize latency.
- Audit Trails: Tracks all changes to toggles and overrides, supporting undo operations.
- Context Aware: Designed to work seamlessly with multi-tenant architectures.
Data Model
The feature toggles system is built around two primary entities that control feature availability.
FeatureToggle
Defines the global state and behavior of a feature flag.
id(uuid): Unique identifier for the toggle.identifier(string): The unique key used in code to check the feature (e.g.,checkout.new_flow).name(string): Human-readable name.description(string?): Optional description of the feature.category(string?): Optional grouping category.type(enum): The data type of the toggle (boolean,string,number,json).defaultValue(jsonb): The global default value used if no specific override matches.
FeatureToggleOverride
Allows granular control over a feature toggle for specific contexts.
id(uuid): Unique identifier for the override.toggle(relation): Reference to the parentFeatureToggle.tenantId(uuid): The tenant this override applies to.value(jsonb): The specific value for this context.
If no
FeatureToggleOverrideentity exists for a specific tenant, the system inherits the value from theFeatureToggle.defaultValue.
Caching Strategy
To ensure high performance and minimal latency, feature flag resolutions are heavily cached.
Resolution Flow
- Cache Check: The system checks for a cached result using the key
feature_toggles:isEnabled:{identifier}:{tenantId}. - Database Lookup (Toggle): If not cached, it fetches the
FeatureToggledefinition from the database. - Database Lookup (Override): It then checks for a
FeatureToggleOverridematching the requested tenant. - Result Construction: The final state is resolved (Override > Default State) and cached.
Cache Tags & Invalidation
Cache entries are tagged to allow for precise invalidation when toggles or overrides are modified.
feature_toggles:toggle:{identifier}feature_toggles:tenant:{tenantId}
Audit Logs
All modifications to feature toggles and overrides are audited to ensure accountability and traceability.
- Action Logs: Creation, updates, deletions, and state changes are logged. The system supports undo operations for these actions, allowing you to revert changes quickly.
- Access Logs: Read access to toggle overrides via the API is logged (
read:list) to track who is verifying feature configurations.
Usage Reference
The system supports strict typing for different feature toggle kinds. Choose the appropriate method for your data type.
Boolean Flags
Use for simple on/off switches.
React Component (FeatureGuard)
Wrapper for conditional rendering. Designed strictly for boolean toggles.
import { FeatureGuard } from '@saasframe/core/modules/feature_toggles/components/FeatureGuard';
<FeatureGuard
id="checkout_flow_v2"
fallback={<div className="alert">Feature disabled</div>}
>
<CheckoutComponent />
</FeatureGuard>
Frontend Hook
const { enabled, isLoading } = useFeatureFlagBoolean({ id: 'checkout_flow_v2' });
Backend Service
const result = await service.getBoolConfig('checkout_flow_v2', tenantId);
if (result.ok && result.value) {
// Feature is enabled
}
String Configuration
Use for dynamic text, labels, or string keys.
Frontend Hook
const { value: title } = useFeatureFlagString({ id: 'home_page_title' });
Backend Service
const result = await service.getStringConfig('home_page_title', tenantId);
Number Configuration
Use for numeric limits, timeouts, or counts.
Frontend Hook
const { value: limit } = useFeatureFlagNumber({ id: 'max_items_per_order' });
Backend Service
const result = await service.getNumberConfig('max_items_per_order', tenantId);
JSON Configuration
Use for complex configuration objects.
Frontend Hook
// Pass a generic type for type safety
const { value: config } = useFeatureFlagJson<PaymentConfig>({ id: 'payment_provider_config' });
Backend Service
const result = await service.getJsonConfig<PaymentConfig>('payment_provider_config', tenantId);
Error Handling
All check operations return a standardized Result<T> structure (backend) or expose error states (frontend).
Result Structure
type Result<T> =
| { ok: true; value: T; resolution: ResolutionMetadata }
| { ok: false; error: ToggleError; resolution: ResolutionMetadata }
Error Codes
-
TYPE_MISMATCH: The toggle exists but is not of the requested type (e.g., requested String but found Boolean). -
MISSING_TOGGLE: The identifier does not exist in the system. -
INVALID_VALUE: The stored value could not be parsed.
API Status Codes
When interacting with the API directly:
200 OK: Successful check.400 Bad Request: Type mismatch or missing parameters.401 Unauthorized: Missing authentication.404 Not Found: Toggle identifier does not exist or tenant context is missing.
Managing Feature Flags in the Admin
Manage your feature toggles through the Admin UI.
Global Feature Toggles
View and manage all global feature flags.

Editing a Feature Toggle
Update the status and details of a feature toggle.

Overrides
Configure specific overrides for feature toggles.
