Context-Aware Configuration
Context-aware configuration allows you to create dynamic feature flags that evaluate differently based on request-specific information.
Context Structure
The context parameter provides request-specific information:
ts
interface VariantContext {
// User identification
userId?: string
sessionId?: string
ipAddress?: string
// User information
user?: {
id?: string
role?: string
isBetaTester?: boolean
[key: string]: any
}
// Device information
device?: {
isMobile?: boolean
[key: string]: any
}
// Additional custom context
[key: string]: any
}Populating Context
Context is automatically extracted from H3 events on the server. Enrich it using server middleware:
ts
// server/middleware/user-context.ts
export default defineEventHandler(async (event) => {
// Get authenticated user
const user = await getUserFromSession(event)
if (user) {
event.context.user = {
id: user.id,
role: user.role,
isBetaTester: user.betaTester,
subscriptionTier: user.subscription
}
}
// Detect device information
const userAgent = getHeader(event, 'user-agent') || ''
event.context.device = {
isMobile: /mobile/i.test(userAgent),
isTablet: /tablet/i.test(userAgent)
}
})Usage Examples
Role-Based Flags
ts
// feature-flags.config.ts
export default defineFeatureFlags((context) => {
return {
// Admin-only features
adminPanel: context?.user?.role === 'admin',
advancedSettings: context?.user?.role === 'admin' || context?.user?.role === 'moderator',
// Role-based feature variants
dashboard: {
enabled: true,
value: context?.user?.role === 'admin' ? 'advanced' : 'basic'
}
}
})Environment-Based Flags
ts
export default defineFeatureFlags((context) => {
const isDev = process.env.NODE_ENV === 'development'
const isProd = process.env.NODE_ENV === 'production'
return {
// Development-only features
devTools: isDev,
debugPanel: isDev,
// Production features with gradual rollout
newCheckout: {
enabled: isProd,
variants: [
{ name: 'old', weight: 80, value: false },
{ name: 'new', weight: 20, value: true }
]
}
}
})User Status-Based Flags
ts
export default defineFeatureFlags((context) => {
const user = context?.user
return {
// Beta tester features
betaFeatures: user?.isBetaTester ?? false,
// Subscription-based features
premiumFeatures: user?.subscriptionTier === 'premium',
proTools: ['pro', 'premium', 'enterprise'].includes(user?.subscriptionTier),
// Account age-based rollout
newUserExperience: {
enabled: true,
value: (user?.accountAge || 0) < 30
}
}
})Device-Based Flags
ts
export default defineFeatureFlags((context) => {
const device = context?.device
return {
// Mobile-specific features
mobileOptimizedUI: device?.isMobile ?? false,
touchGestures: device?.isMobile || device?.isTablet,
// Desktop-only features
keyboardShortcuts: !device?.isMobile,
// Responsive feature variants
imageQuality: {
enabled: true,
value: device?.isMobile ? 'medium' : 'high'
}
}
})Fallback Behavior
When context is unavailable, flags gracefully fall back to default values:
ts
export default defineFeatureFlags((context) => {
return {
// Using nullish coalescing for safe defaults
adminPanel: context?.user?.role === 'admin', // false if undefined
// Explicit fallback values
betaFeatures: context?.user?.isBetaTester ?? false,
maxItems: context?.user?.preferences?.itemsPerPage ?? 20,
// Fallback chains
userId: context?.user?.id || context?.userId || 'anonymous'
}
})Best Practices
- Always use optional chaining (
?.) when accessing context properties - Provide sensible defaults with nullish coalescing (
??) - Test your flags with and without context
- Document which flags require context and their fallback behavior
- Populate
context.user.idin middleware for consistent variant assignment