Skip to main content

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โ€‹

  1. Navigate to /admin
  2. Go to System Administration โ†’ FeatureFlag
  3. Create, edit, or toggle flags
  4. 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โ€‹

  1. Use descriptive keys - Use kebab-case like new-dashboard or test-mode
  2. Clear cache after updates - Cache is auto-cleared by AdminJS, but manual clears available via clearFeatureFlagCache()
  3. Initialize on startup - Call initializeFeatureFlagCache() in your app startup for better performance
  4. Default to disabled - New flags should default to false for safety
  5. 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.