@molecule/api-feature-flags
Core interface · feature-flags · API (Node) · v1.0.3 · Apache-2.0
Feature flag management core interface for molecule.dev — flag evaluation, targeting rules, and percentage rollouts
npm install @molecule/api-feature-flagsHow it works
@molecule/api-feature-flags is the feature-flags core interface on the API (Node) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 1 provider: @molecule/api-feature-flags-database.
import { setProvider, isEnabled, setFlag, evaluateForUser } from '@molecule/api-feature-flags'
import { provider } from '@molecule/api-feature-flags-database'
// Wire the provider at startup
setProvider(provider)
// Create a feature flag with percentage rollout
await setFlag({ name: 'new-dashboard', enabled: true, percentage: 50 })
// Check if a flag is enabled for a user
const enabled = await isEnabled('new-dashboard', { userId: 'user-123' })
// Evaluate all flags for a user
const flags = await evaluateForUser('user-123')Providers (1): @molecule/api-feature-flags-database
Works with: @molecule/api-bond, @molecule/api-i18n
Reference
Feature flags core interface for molecule.dev.
Provides the FeatureFlagProvider interface for feature flag management
including flag evaluation, CRUD operations, rule-based targeting, and
percentage rollouts. Bond a concrete provider
(e.g. @molecule/api-feature-flags-database) at startup via setProvider().
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider, isEnabled, setFlag, evaluateForUser } from '@molecule/api-feature-flags'
import { provider } from '@molecule/api-feature-flags-database'
// Wire the provider at startup
setProvider(provider)
// Create a feature flag with percentage rollout
await setFlag({ name: 'new-dashboard', enabled: true, percentage: 50 })
// Check if a flag is enabled for a user
const enabled = await isEnabled('new-dashboard', { userId: 'user-123' })
// Evaluate all flags for a user
const flags = await evaluateForUser('user-123')
Type
core
Installation
npm install @molecule/api-feature-flags @molecule/api-bond @molecule/api-i18n
API
Interfaces
FlagRule
A targeting rule for a feature flag. Rules are evaluated against the provided context to determine if a flag is enabled for a specific user or request.
interface FlagRule {
/** The context attribute to evaluate (e.g. 'plan', 'country'). */
attribute: string
/** The comparison operator. */
operator: FlagOperator
/** The value to compare against. */
value: unknown
}
FlagContext
Evaluation context for feature flag checks. Provides user identity and arbitrary attributes for rule-based targeting.
interface FlagContext {
/** The user identifier for user-specific targeting. */
userId?: string
/** Arbitrary attributes for rule evaluation. */
attributes?: Record<string, unknown>
}
FeatureFlag
A feature flag definition.
interface FeatureFlag {
/** The unique flag name/key. */
name: string
/** Whether the flag is globally enabled. */
enabled: boolean
/** Human-readable description of the flag's purpose. */
description?: string
/** Targeting rules. When present, the flag is enabled only if all rules match. */
rules?: FlagRule[]
/** Percentage rollout (0–100). When set, only the given percentage of users see the flag. */
percentage?: number
/** When the flag was created. */
createdAt: Date
/** When the flag was last updated. */
updatedAt: Date
}
FeatureFlagUpdate
Payload for creating or updating a feature flag.
interface FeatureFlagUpdate {
/** The unique flag name/key. */
name: string
/** Whether the flag is globally enabled. */
enabled: boolean
/** Human-readable description of the flag's purpose. */
description?: string
/** Targeting rules. */
rules?: FlagRule[]
/** Percentage rollout (0–100). */
percentage?: number
}
FeatureFlagProvider
Feature flag provider interface.
All feature flag providers must implement this interface to provide flag evaluation, CRUD operations, rule-based targeting, and percentage rollouts.
interface FeatureFlagProvider {
/**
* Checks whether a flag is enabled for the given context.
* Evaluates targeting rules and percentage rollouts.
*
* @param flag - The flag name/key to check.
* @param context - Optional evaluation context with user and attributes.
* @returns `true` if the flag is enabled for the given context.
*/
isEnabled(flag: string, context?: FlagContext): Promise<boolean>
/**
* Retrieves a flag definition by name.
*
* @param flag - The flag name/key.
* @returns The flag definition, or `null` if not found.
*/
getFlag(flag: string): Promise<FeatureFlag | null>
/**
* Creates or updates a feature flag.
*
* @param flag - The flag data to create or update.
* @returns The created or updated flag definition.
*/
setFlag(flag: FeatureFlagUpdate): Promise<FeatureFlag>
/**
* Retrieves all feature flags.
*
* @returns Array of all flag definitions.
*/
getAllFlags(): Promise<FeatureFlag[]>
/**
* Deletes a feature flag.
*
* @param flag - The flag name/key to delete.
*/
deleteFlag(flag: string): Promise<void>
/**
* Evaluates multiple flags for a specific user. Returns a map of
* flag names to their enabled state.
*
* @param userId - The user identifier.
* @param flags - Optional list of flag names to evaluate. If omitted, evaluates all flags.
* @returns A record mapping flag names to their enabled state.
*/
evaluateForUser(userId: string, flags?: string[]): Promise<Record<string, boolean>>
}
Types
FlagOperator
Comparison operators for flag targeting rules.
type FlagOperator = 'eq' | 'neq' | 'in' | 'notIn' | 'gt' | 'lt'
Functions
setProvider(provider)
Registers a feature flag provider as the active singleton. Called by bond packages during application startup.
function setProvider(provider: FeatureFlagProvider): void
provider— The feature flag provider implementation to bond.
getProvider()
Retrieves the bonded feature flag provider, throwing if none is configured.
function getProvider(): FeatureFlagProvider
Returns: The bonded feature flag provider.
hasProvider()
Checks whether a feature flag provider is currently bonded.
function hasProvider(): boolean
Returns: true if a feature flag provider is bonded.
isEnabled(flag, context)
Checks whether a flag is enabled for the given context using the bonded provider.
function isEnabled(flag: string, context?: FlagContext): Promise<boolean>
flag— The flag name/key to check.context— Optional evaluation context.
Returns: true if the flag is enabled for the given context.
getFlag(flag)
Retrieves a flag definition by name using the bonded provider.
function getFlag(flag: string): Promise<FeatureFlag | null>
flag— The flag name/key.
Returns: The flag definition, or null if not found.
setFlag(flag)
Creates or updates a feature flag using the bonded provider.
function setFlag(flag: FeatureFlagUpdate): Promise<FeatureFlag>
flag— The flag data to create or update.
Returns: The created or updated flag definition.
getAllFlags()
Retrieves all feature flags using the bonded provider.
function getAllFlags(): Promise<FeatureFlag[]>
Returns: Array of all flag definitions.
deleteFlag(flag)
Deletes a feature flag using the bonded provider.
function deleteFlag(flag: string): Promise<void>
flag— The flag name/key to delete.
Returns: Resolves when the flag is removed.
evaluateForUser(userId, flags)
Evaluates multiple flags for a specific user using the bonded provider.
function evaluateForUser(userId: string, flags?: string[]): Promise<Record<string, boolean>>
userId— The user identifier.flags— Optional list of flag names to evaluate.
Returns: A record mapping flag names to their enabled state.
Available Providers
| Provider | Package |
|---|---|
| Feature Flags | @molecule/api-feature-flags-database |
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1@molecule/api-i18n^1.0.1
Runtime Dependencies
-
@molecule/api-bond -
@molecule/api-i18n -
Percentage/rule targeting only applies when you pass context.
isEnabled('new-dashboard')with NO{ userId }falls back to the bare global toggle — for a percentage flag that means EVERYONE gets it. Always pass the authenticated user's id for user-facing gating. -
Evaluate flags SERVER-SIDE and ship booleans. Expose an endpoint that returns
evaluateForUser(userId)results for the UI to consume — never send raw flag definitions/rules to the client or re-implement targeting there. -
A client-side flag gate is UX, not security. Anything the flag protects must also be gated on the API route with the same check.
-
Rollouts must be sticky per user. Evaluate with the same stable
userIdevery time — a session id (or none) makes features flicker between requests. -
setFlag()is an upsert keyed onname. Flag CRUD (setFlag,deleteFlag,getAllFlags) is an admin surface — put it behind an admin authorizer, not a public route.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- A flag actually GATES behavior: with the flag OFF the feature it
guards is hidden/disabled in the UI; flip it ON from the admin screen (or
setFlag) and reload — the feature appears with no code change or rebuild. Turn it back OFF and it disappears again. - Targeting evaluates PER USER: for a rule- or percentage-flag, a user
inside the segment (matching
attributes/rollout) sees the feature and a user outside it does not — verify by signing in as each and viaisEnabled(flag, { userId, attributes })/evaluateForUser(userId)returning the right boolean for each. The same user's result is sticky across reloads, not flickering between requests. - An UNDEFINED flag (never created) evaluates to the SAFE default:
isEnabled('does-not-exist', ctx)returns false and the guarded feature stays hidden — it does NOT throw or fall open. - Flag reads are cheap on the hot path — evaluation is server-side and cached, not a DB round-trip per render — and a toggle propagates promptly (within a reload / short cache window), not only after a restart.
- The gate is enforced SERVER-SIDE, not just in the UI: calling the API route the flag protects with the flag OFF is rejected, not merely hidden (a client-side flag gate is UX, not security).
- ADMIN-ONLY writes: only an authorized admin can create/toggle/delete
flags. A normal signed-in user hitting the flag-CRUD endpoints
(
setFlag/deleteFlag/getAllFlags) is rejected — they can't flip a flag or read raw flag definitions/rules through any exposed route.