@subflag/openfeature-web-provider
v0.2.0
Published
OpenFeature web provider for Subflag feature flags
Maintainers
Readme
@subflag/openfeature-web-provider
OpenFeature web provider for Subflag feature flags.
How it works: This provider pre-fetches all flags during initialization and serves them synchronously from an in-memory cache. Flags are fetched once when the provider is set, making flag evaluations instant with zero network latency.
Installation
npm install @subflag/openfeature-web-provider @openfeature/web-sdkor with pnpm:
pnpm add @subflag/openfeature-web-provider @openfeature/web-sdkQuick Start
import { OpenFeature } from '@openfeature/web-sdk';
import { SubflagWebProvider } from '@subflag/openfeature-web-provider';
// Initialize the provider
const provider = new SubflagWebProvider({
apiUrl: 'http://localhost:8080',
apiKey: 'sdk-production-my-app-your-key-here',
});
// Set provider and wait for it to be ready
await OpenFeature.setProviderAndWait(provider);
// Get a client
const client = OpenFeature.getClient();
// Evaluate flags - synchronous, no network call!
const isEnabled = client.getBooleanValue('new-feature', false);
const bannerText = client.getStringValue('banner-text', 'Welcome!');
const maxItems = client.getNumberValue('max-items', 10);
const config = client.getObjectValue('ui-config', { theme: 'light' });Configuration
SubflagProviderConfig
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| apiUrl | string | Yes | The Subflag API URL (e.g., "http://localhost:8080") |
| apiKey | string | Yes | Your SDK API key (format: sdk-{env}-{random}) |
| timeout | number | No | Request timeout in milliseconds (default: 5000) |
Getting an API Key
See Team Management → API Keys in the docs for instructions on creating API keys.
How It Works: Flag Caching
The web provider uses a pre-fetch and cache strategy:
- Initialization: When
setProviderAndWait()is called, the provider fetches ALL flags from the server using/sdk/evaluate-all - Caching: All flags are stored in an in-memory Map by flag key
- Synchronous Evaluation: Flag lookups are instant - no network calls during evaluation
- Refresh: Call
await provider.initialize()again to refresh the cache
This approach provides:
- ✅ Zero-latency flag evaluations (synchronous, no await needed)
- ✅ Reduced server load (one bulk request vs N individual requests)
- ✅ Offline capability (flags work even if server becomes unreachable)
- ⚠️ Eventual consistency (flags may be stale until next refresh)
Usage with React
import { useEffect, useState } from 'react';
import { OpenFeature } from '@openfeature/web-sdk';
import { SubflagWebProvider } from '@subflag/openfeature-web-provider';
function App() {
const [client, setClient] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function initializeProvider() {
try {
const provider = new SubflagWebProvider({
apiUrl: import.meta.env.VITE_SUBFLAG_API_URL,
apiKey: import.meta.env.VITE_SUBFLAG_API_KEY,
});
// This pre-fetches all flags from the server
await OpenFeature.setProviderAndWait(provider);
setClient(OpenFeature.getClient());
} catch (error) {
console.error('Failed to initialize provider:', error);
} finally {
setLoading(false);
}
}
initializeProvider();
}, []);
if (loading) return <div>Loading flags...</div>;
if (!client) return <div>Error loading flags</div>;
return <FeatureGatedComponent client={client} />;
}
function FeatureGatedComponent({ client }) {
// Flags are evaluated synchronously from cache - no await needed!
const enabled = client.getBooleanValue('new-feature', false);
return enabled ? <NewFeature /> : <OldFeature />;
}Refreshing Flags
To update flags without reloading the page:
import { OpenFeature } from '@openfeature/web-sdk';
// Get the current provider
const provider = OpenFeature.getProviderForClient();
// Re-fetch all flags from the server
await provider.initialize();
// Flags are now updated in the cacheSupported Flag Types
The provider supports all OpenFeature value types:
- Boolean:
getBooleanValue(flagKey, defaultValue) - String:
getStringValue(flagKey, defaultValue) - Number:
getNumberValue(flagKey, defaultValue) - Object:
getObjectValue(flagKey, defaultValue)
Error Handling
The provider handles errors gracefully and returns default values when:
- The flag doesn't exist (404)
- The API key is invalid (401/403)
- Network errors occur
- The flag value type doesn't match the requested type
const client = OpenFeature.getClient();
// If 'missing-flag' doesn't exist, returns false
const value = client.getBooleanValue('missing-flag', false);
// You can also get detailed evaluation information
const details = client.getBooleanDetails('my-flag', false);
console.log(details.reason); // 'STATIC', 'DEFAULT', or 'ERROR'
console.log(details.variant); // Variant name (e.g., 'control', 'treatment')
console.log(details.errorCode); // Error code if reason is 'ERROR'Development
# Install dependencies
pnpm install
# Build the provider
pnpm build
# Run tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Type check
pnpm typecheckLicense
MIT
