Skip to Content
Client-Side Code ExamplesJavaScript / TypeScript

JavaScript / TypeScript

Universal JavaScript / TypeScript integration for browser environments. This code example provides a lightweight, type-safe way to integrate DarkFeature into your client-side JavaScript applications.

Requirements

Installation

NPM/Yarn/PNPM

npm install @darkfeature/sdk-javascript # or yarn add @darkfeature/sdk-javascript # or pnpm add @darkfeature/sdk-javascript

ES6 Module Import

import { DarkFeatureClient } from '@darkfeature/sdk-javascript'

CDN (Browser)

<!-- Option 1: ES6 Modules (Modern browsers) --> <script type="module"> import { DarkFeatureClient } from 'https://cdn.skypack.dev/@darkfeature/sdk-javascript' </script> <!-- Option 2: Global variable via esm.sh (works in all browsers) --> <script src="https://esm.sh/@darkfeature/sdk-javascript?bundle&global=DarkFeature"></script>

Quick Start

Basic Usage

import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: 'your-client-api-key-here', // Store in environment variable context: { userId: '123', email: '[email protected]', version: '1.0.0', }, }) // Get a single feature flag with fallback const isEnabled = await client.getFeature('new-navigation', { fallback: false }) if (isEnabled) { console.log('New navigation is enabled!') } else { console.log('Using old navigation') }

With Context Override

// Get a feature flag with context override const featureValue = await client.getFeature('premium-features', { fallback: 'control', context: { segment: 'premium' }, }) console.log('Feature value:', featureValue)

API Reference

Constructor

const client = new DarkFeatureClient(config)

Parameters:

  • config (object) - Configuration object

Config Options:

  • apiKey (string) - Your DarkFeature client API key
  • context (object) - Default user context for all requests
  • baseUrl (string, optional) - Custom API endpoint
  • timeout (number, optional) - Request timeout in milliseconds
  • cache (object, optional) - Caching configuration
  • debug (boolean, optional) - Enable debug logging

Methods

getFeature(featureKey, options)

Get a single feature flag value.

// Basic usage with fallback const isEnabled = await client.getFeature('new-feature', { fallback: false }) // With context override const value = await client.getFeature('feature-name', { fallback: 'default-value', context: { segment: 'premium' }, }) // With custom fallback const config = await client.getFeature('api-config', { fallback: { timeout: 5000, retries: 3 }, }) // With timeout const result = await client.getFeature('slow-feature', { fallback: false, timeout: 1000, })

getFeatures(features, options)

Get multiple feature flags at once.

// Get multiple features const features = await client.getFeatures({ features: { 'new-navigation': false, 'premium-features': 'default-value', 'api-config': null, }, context: { userId: '456', // Override default context for this request }, }) console.log(features) // { 'new-navigation': true, 'premium-features': 'variant-a', 'api-config': { ... } }

setContext(context)

Update the default context for all subsequent requests.

// Update context client.setContext({ userId: 'new-user-123', email: '[email protected]', plan: 'premium', }) // All subsequent getFeature calls will use this context const isEnabled = await client.getFeature('premium-features', { fallback: false, })

getContext()

Get the current default context.

const context = client.getContext() console.log('Current context:', context)

clearContext()

Clear the default context.

client.clearContext() // Subsequent calls will not include user context

isReady()

Check if the client is ready to make requests.

if (client.isReady()) { console.log('Client is ready!') } else { console.log('Client is still initializing...') }

Browser Integration

Vanilla JavaScript

// app.js import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_CLIENT_API_KEY, context: { userId: getCurrentUserId(), email: getCurrentUserEmail(), }, }) // Initialize features async function initializeFeatures() { const features = await client.getFeatures({ features: { 'new-navigation': false, 'dark-mode': false, 'premium-features': false, }, }) applyFeatures(features) } function applyFeatures(features) { if (features['new-navigation']) { document.body.classList.add('new-nav') } if (features['dark-mode']) { document.body.classList.add('dark-theme') } if (features['premium-features']) { showPremiumFeatures() } } // Initialize on page load document.addEventListener('DOMContentLoaded', initializeFeatures)

React Integration

// useFeatureFlag.js import { useState, useEffect } from 'react' import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.REACT_APP_DARKFEATURE_API_KEY, context: { userId: '123' }, }) export function useFeatureFlag(featureName, fallback = false) { const [isEnabled, setIsEnabled] = useState(fallback) const [loading, setLoading] = useState(true) useEffect(() => { async function checkFeature() { try { const result = await client.getFeature(featureName, { fallback }) setIsEnabled(result) } catch (error) { console.error('Feature flag error:', error) setIsEnabled(fallback) } finally { setLoading(false) } } checkFeature() }, [featureName, fallback]) return { isEnabled, loading } } // Component usage function MyComponent() { const { isEnabled, loading } = useFeatureFlag('new-feature', false) if (loading) return <div>Loading...</div> return isEnabled ? <NewFeature /> : <OldFeature /> }

Vue.js Integration

// featureFlags.js import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.VUE_APP_DARKFEATURE_API_KEY, context: { userId: '123' }, }) export const featureFlags = { async getFeature(featureName, fallback = false) { try { return await client.getFeature(featureName, { fallback }) } catch (error) { console.error('Feature flag error:', error) return fallback } }, async getFeatures(features) { try { return await client.getFeatures({ features }) } catch (error) { console.error('Feature flags error:', error) return features // Return fallback values } }, } // Vue component export default { data() { return { isNewFeatureEnabled: false, } }, async mounted() { this.isNewFeatureEnabled = await featureFlags.getFeature( 'new-feature', false, ) }, template: ` <div> <div v-if="isNewFeatureEnabled">New Feature</div> <div v-else>Old Feature</div> </div> `, }

Advanced Configuration

Environment Variables

// .env REACT_APP_DARKFEATURE_API_KEY = your - client - api - key VUE_APP_DARKFEATURE_API_KEY = your - client - api - key // app.js const client = new DarkFeatureClient({ apiKey: process.env.REACT_APP_DARKFEATURE_API_KEY || process.env.VUE_APP_DARKFEATURE_API_KEY, baseUrl: 'https://api.darkfeature.com', timeout: 5000, cache: { enabled: true, ttl: 300000, // 5 minutes maxSize: 1000, }, debug: process.env.NODE_ENV === 'development', })

Caching Strategy

const client = new DarkFeatureClient({ apiKey: 'your-api-key', cache: { enabled: true, ttl: 300000, // 5 minutes maxSize: 1000, }, }) // Features will be cached in browser storage const isEnabled = await client.getFeature('new-feature', { fallback: false })

Error Handling

import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_CLIENT_API_KEY, }) async function getFeatureWithFallback(featureName, fallback = false) { try { return await client.getFeature(featureName, { fallback }) } catch (error) { console.error(`Error getting feature ${featureName}:`, error) return fallback } } // Usage const isEnabled = await getFeatureWithFallback('new-feature', false)

Performance Optimization

Batch Requests

// Get multiple features in one request const features = await client.getFeatures({ features: { 'feature-a': false, 'feature-b': false, 'feature-c': false, }, context: { userId: '123' }, }) // Use features if (features['feature-a']) { // Feature A logic } if (features['feature-b']) { // Feature B logic }

Lazy Loading

// Load features only when needed class FeatureManager { constructor() { this.client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_CLIENT_API_KEY, }) this.cache = new Map() } async getFeature(featureName, fallback = false) { if (this.cache.has(featureName)) { return this.cache.get(featureName) } const result = await this.client.getFeature(featureName, { fallback }) this.cache.set(featureName, result) return result } } const featureManager = new FeatureManager()

Testing

import { DarkFeatureClient } from '@darkfeature/sdk-javascript' // Mock client for testing class MockDarkFeatureClient extends DarkFeatureClient { constructor(mockFlags = {}) { super({ apiKey: 'test-key' }) this.mockFlags = mockFlags } async getFeature(featureName, options = {}) { return this.mockFlags[featureName] ?? options.fallback } async getFeatures(features, options = {}) { const result = {} for (const [key, fallback] of Object.entries(features)) { result[key] = this.mockFlags[key] ?? fallback } return result } } // Test usage const mockClient = new MockDarkFeatureClient({ 'new-navigation': true, 'premium-features': false, }) const isEnabled = await mockClient.getFeature('new-navigation', { fallback: false, }) console.log(isEnabled) // true

Best Practices

1. Environment Configuration

// config/features.js import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const createFeatureClient = () => { return new DarkFeatureClient({ apiKey: process.env.REACT_APP_DARKFEATURE_API_KEY || process.env.VUE_APP_DARKFEATURE_API_KEY, baseUrl: 'https://api.darkfeature.com', timeout: 5000, cache: { enabled: true, ttl: 300000, maxSize: 1000, }, debug: process.env.NODE_ENV === 'development', }) } export default createFeatureClient

2. Error Handling

// utils/features.js import { DarkFeatureClient } from '@darkfeature/sdk-javascript' const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_CLIENT_API_KEY, }) export async function getFeatureSafely(featureName, fallback = false) { try { return await client.getFeature(featureName, { fallback }) } catch (error) { console.error(`Feature flag error for ${featureName}:`, error) return fallback } } export async function getFeaturesSafely(features, context = {}) { try { return await client.getFeatures({ features, context }) } catch (error) { console.error('Feature flags error:', error) return features // Return fallback values } }

3. Context Management

// utils/context.js export function createUserContext(user) { return { userId: user?.id?.toString(), email: user?.email?.toLowerCase(), plan: user?.plan || 'free', userAgent: navigator.userAgent, language: navigator.language, } } // Usage const context = createUserContext(currentUser) client.setContext(context)

Security Considerations

API Key Management

// Use client-side API keys only const client = new DarkFeatureClient({ apiKey: process.env.DARKFEATURE_CLIENT_API_KEY, // ✅ Client-side key // apiKey: process.env.DARKFEATURE_SERVER_API_KEY, // ❌ Never use server keys in browser })

User Context Validation

// Validate and sanitize user context const sanitizeContext = user => { return { userId: user?.id?.toString(), email: user?.email?.toLowerCase(), plan: ['free', 'pro', 'enterprise'].includes(user?.plan) ? user.plan : 'free', } } const context = sanitizeContext(currentUser) client.setContext(context)
Last updated on