High-performance feature flag system for Cloudflare Workers with <5ms evaluation latency, canary deployments, A/B testing, and tenant isolation.
npm install @stackbilt/feature-flagsAdd KV namespaces to your wrangler.toml:
[[kv_namespaces]]
binding = "FEATURE_FLAGS_KV"id = "your-flags-kv-id"
[[kv_namespaces]]
binding = "FEATURE_FLAGS_CACHE"id = "your-cache-kv-id"import{createProductionFeatureFlags,createFeatureFlagMiddleware}from'@stackbilt/feature-flags';import{Hono}from'hono';constapp=newHono();// SetupconstflagSystem=createProductionFeatureFlags(env);app.use('*',createFeatureFlagMiddleware({manager: flagSystem.manager}));// Evaluate in routesapp.get('/api/data',async(c)=>{constflags=c.get('featureFlags');constuseNewFeature=awaitflags.isEnabled('new_feature');returnc.json({data: useNewFeature ? getNewData() : getLegacyData()});});- Boolean flags — simple on/off toggles
- Percentage rollouts — gradual rollouts with consistent user assignment
- Variant flags — A/B/n testing with weighted variants
- Canary deployments — staged rollouts with automatic advancement
- Tenant overrides — per-tenant flag values
- Rule engine — target by tenant, user, IP, custom attributes, time windows
- KV storage — built for Cloudflare Workers KV
- Hono middleware — drop-in integration with route guards and variant routing
- Admin API — REST endpoints for flag management
- Audit logging — track all flag changes
- Metrics — evaluation counts, latency percentiles, variant distribution
import{createCanaryFlags}from'@stackbilt/feature-flags';const{ gradualRollout, canaryConfig }=createCanaryFlags();awaitmanager.createFlag(gradualRollout);// Start at 5%, auto-advance through stagesawaitmanager.startCanaryDeployment('gradual_rollout_feature',canaryConfig);// Manual advanceawaitmanager.updateCanaryDeployment('gradual_rollout_feature',{percentage: 25});// Emergency rollbackawaitmanager.stopCanaryDeployment('gradual_rollout_feature');import{createABTestFlags}from'@stackbilt/feature-flags';const{ simpleAB, multiVariant }=createABTestFlags();awaitmanager.createFlag(simpleAB);// Evaluate — returns consistent variant per userconstresult=awaitmanager.evaluate('simple_ab_test',{userId: 'user-123'});console.log(result.variant);// 'control' or 'variant_a'console.log(result.value);// { theme: 'default', buttonColor: 'blue' }import{withVariant}from'@stackbilt/feature-flags';app.get('/recommendations',withVariant('recommendation_algo',{control: controlHandler,ml_enhanced: mlHandler,personalized: personalizedHandler}));import{ConditionBuilder}from'@stackbilt/feature-flags';constflag={flagId: 'premium_feature',name: 'Premium Feature',type: 'boolean'asconst,status: 'enabled'asconst,defaultValue: false,rules: [{id: 'premium_only',conditions: [ConditionBuilder.customAttribute('plan','equals','premium'),ConditionBuilder.tenantIn(['tenant-a','tenant-b'])],priority: 1,enabled: true,value: true}]};| Builder | Description |
|---|---|
tenantEquals(id) | Match specific tenant |
tenantIn(ids) | Match any of listed tenants |
userEquals(id) | Match specific user |
customAttribute(key, op, value) | Match custom attribute |
ipStartsWith(prefix) | Match IP prefix |
userAgentContains(str) | Match user agent substring |
timeAfter(timestamp) | After timestamp |
timeBefore(timestamp) | Before timestamp |
equals, not_equals, in, not_in, contains, not_contains, starts_with, ends_with, greater_than, greater_than_or_equal, less_than, less_than_or_equal, matches_regex, exists, not_exists
import{createFeatureFlagMiddleware,requireFlag,withFlag,checkFlag}from'@stackbilt/feature-flags';// Global middleware — attaches flag client to contextapp.use('*',createFeatureFlagMiddleware({ manager }));// Route guard — 404 if flag is offapp.get('/beta/*',requireFlag('beta_access'),handler);// Conditional handlerapp.get('/api/data',withFlag('use_new_api',newHandler,legacyHandler));// Manual checkapp.get('/api/status',async(c)=>{constenabled=awaitcheckFlag(c,'enhanced_status');// ...});import{createAdminRouter}from'@stackbilt/feature-flags';constadmin=createAdminRouter({
manager,corsHeaders: true,authMiddleware: myAuthMiddleware});app.route('/admin/flags',admin);| Method | Path | Description |
|---|---|---|
GET | /flags | List all flags |
POST | /flags | Create flag |
GET | /flags/:id | Get flag |
PUT | /flags/:id | Update flag |
DELETE | /flags/:id | Delete flag |
POST | /flags/:id/evaluate | Test evaluation |
POST | /flags/:id/canary | Start canary |
POST | /flags/:id/canary/advance | Advance canary % |
POST | /flags/:id/canary/rollback | Rollback canary |
GET | /flags/:id/metrics | Get metrics |
GET | /audit | Get audit logs |
| Function | Description |
|---|---|
createFeatureFlagSystem(config) | Full setup with custom config |
createProductionFeatureFlags(env) | Production defaults (5min cache, 2s timeout) |
createDevelopmentFeatureFlags(env) | Dev defaults (1min cache, 10s timeout) |
createLightweightFeatureFlags(env) | No cache, no metrics |
createTestFeatureFlags() | In-memory mock for tests |
import{createTestFeatureFlags}from'@stackbilt/feature-flags';constsystem=createTestFeatureFlags();awaitsystem.manager.createFlag({flagId: 'test_feature',name: 'Test',type: 'boolean',status: 'enabled',defaultValue: true});constresult=awaitsystem.manager.evaluateBoolean('test_feature',{tenantId: 'test'});// result === trueimport{createSimpleFlags,createCanaryFlags,createABTestFlags,createKillSwitches,createPerformanceFlags,createEnvironmentFlags}from'@stackbilt/feature-flags';Apache-2.0