Feature Flags
A database-backed feature flag system for controlling feature availability and enabling safe testing in production environments.
Overviewโ
The feature flag system allows you to dynamically enable or disable features without deploying new code. This is useful for:
- Testing in production - Enable test mode to prevent destructive operations
- Gradual rollouts - Enable features for testing before full release
- Emergency switches - Quickly disable problematic features
- A/B testing - Control feature availability for different scenarios
Architectureโ
- Database: PostgreSQL table with Prisma ORM
- Caching: In-memory cache for performance (auto-invalidated on updates)
- Admin UI: AdminJS interface at
/adminโ System Administration โ FeatureFlag - API: REST endpoints at
/api/features/v1/
Using Feature Flagsโ
Backend - Controllers & Servicesโ
Check if a feature is enabled:
import { isFeatureEnabled } from 'utils/featureFlags';
async function updateFulfil(data) {
const testMode = await isFeatureEnabled('test-mode');
if (testMode) {
console.log('Test mode enabled - skipping Fulfil update');
return { success: true, skipped: true };
}
// Proceed with actual Fulfil update
await fulfilApi.update(data);
}
Frontend - React Componentsโ
Use the React hook to conditionally render UI:
import { useFeatureFlag } from 'hooks/useFeatureFlag';
function NewDashboard() {
const { enabled, loading } = useFeatureFlag('new-dashboard');
if (loading) return <div>Loading...</div>;
if (!enabled) return null;
return <div>New Dashboard Feature</div>;
}
API Endpointsโ
Check flags via HTTP requests:
# Check if a specific flag is enabled
GET /api/features/v1/check/test-mode
# Response: { "key": "test-mode", "enabled": false }
# Get all feature flags
GET /api/features/v1/all
# Response: { "flags": [...] }
Managing Flagsโ
Admin UIโ
- Navigate to
/admin - Go to System Administration โ FeatureFlag
- Create, edit, or toggle flags
- Changes take effect immediately (cache auto-clears)
Seed Scriptโ
Seed default flags for development:
# Seed just feature flags
npx tsx prisma/seed/run-featureflags.ts
# Or seed all dev data (includes feature flags)
npm run db:seed:dev
Database Schemaโ
CREATE TABLE "FeatureFlag" (
"id" SERIAL PRIMARY KEY,
"key" VARCHAR(100) UNIQUE NOT NULL,
"name" VARCHAR(100) NOT NULL,
"description" TEXT,
"enabled" BOOLEAN DEFAULT false,
"createdAt" TIMESTAMP(6) DEFAULT now(),
"updatedAt" TIMESTAMP(6),
"createdBy" VARCHAR(50),
"updatedBy" VARCHAR(50)
);
Built-in Flagsโ
test-modeโ
Key: test-mode
Description: When enabled, prevents external system updates and destructive operations to allow safe testing in production environments
Use Cases:
- Skip Fulfil API updates when testing
- Prevent email notifications during testing
- Disable payment processing for test orders
- Block shipping label generation
Example:
const testMode = await isFeatureEnabled('test-mode');
if (testMode) {
logger.info('Test mode enabled - skipping external API call');
return mockResponse();
}
Best Practicesโ
- Use descriptive keys - Use kebab-case like
new-dashboardortest-mode - Clear cache after updates - Cache is auto-cleared by AdminJS, but manual clears available via
clearFeatureFlagCache() - Initialize on startup - Call
initializeFeatureFlagCache()in your app startup for better performance - Default to disabled - New flags should default to
falsefor safety - Document flags - Add clear descriptions explaining what each flag controls
Performanceโ
- In-memory cache - Flags are cached after first lookup
- No DB hits - Subsequent checks use cached values
- Auto-invalidation - Cache clears automatically when flags are updated via AdminJS
- Fast lookups - ~1ms for cached flag checks
Testingโ
The feature flag system includes comprehensive test coverage:
- API Tests - 10 tests for REST endpoints
- Controller Tests - 7 tests for controller logic
- Utility Tests - 11 tests for helpers and middleware
Run tests:
npm test -- features
API Referenceโ
Backend Functionsโ
// Check if flag is enabled
isFeatureEnabled(key: string): Promise<boolean>
// Cache management
clearFeatureFlagCache(): void
initializeFeatureFlagCache(): Promise<void>
Frontend Hookโ
useFeatureFlag(key: string): {
enabled: boolean;
loading: boolean;
error: Error | null;
}
REST APIโ
GET /api/features/v1/check/:key
Response: FeatureFlagCheckResponse {
key: string;
enabled: boolean;
}
GET /api/features/v1/all
Response: FeatureFlagsAllResponse {
flags: FeatureFlag[];
}
Implementation Detailsโ
File Locations:
- Model:
src/models/featureflag/ - API:
src/routers/api/features/v1/ - Admin:
src/routers/admin/resources/featureflag.ts - Utils:
src/utils/featureFlags.ts - Hook:
src/hooks/useFeatureFlag.ts - Schema:
prisma/schema.prisma - Seed:
prisma/seed/seed-featureflags.ts
Dependencies: None - Uses existing Prisma, AdminJS, Express, and Zod infrastructure.