UNPKG

@varia-bly/variably-sdk

Version:

Official JavaScript/TypeScript SDK for Variably feature flags, experimentation, and real-time dynamic configurations

330 lines (278 loc) • 9.36 kB
/** * Dynamic Configuration Client Usage Examples * * This example demonstrates how to use the Variably SDK's dynamic configuration * client with real-time updates and intelligent fallback to polling. */ import { DynamicConfigClient, DynamicConfigClientConfig, UserContext, DynamicConfigResult, ConfigChangeCallback } from '../src/index'; // Example 1: Basic Usage with Real-time Updates async function basicRealtimeExample() { console.log('=== Basic Real-time Dynamic Config Example ==='); // Configuration for the client const config: DynamicConfigClientConfig = { apiKey: 'your-api-key-here', jwtToken: 'your-jwt-token-here', // Required for WebSocket authentication projectId: 'your-project-id', baseUrl: 'https://graphql.variably.tech', enableRealtime: true, debug: true }; // Create client const configClient = new DynamicConfigClient(config); // User context for configuration evaluation const userContext: UserContext = { userId: 'user-123', email: 'user@example.com', country: 'US', attributes: { plan: 'premium', beta_user: true } }; try { // Get a boolean configuration const featureEnabled = await configClient.getConfigBool( 'new-dashboard-feature', false, userContext ); console.log('New dashboard feature enabled:', featureEnabled); // Get a JSON configuration const themeConfig = await configClient.getConfigJSON( 'ui-theme-config', { theme: 'light', primaryColor: '#007bff' }, userContext ); console.log('Theme configuration:', themeConfig); // Get a string configuration const apiEndpoint = await configClient.getConfigString( 'api-endpoint', 'https://api.example.com', userContext ); console.log('API endpoint:', apiEndpoint); // Subscribe to real-time changes const unsubscribe = configClient.onConfigChange('new-dashboard-feature', (result) => { console.log('šŸ”„ Configuration changed!', { key: result.key, newValue: result.value, version: result.version, realTime: result.realTimeUpdate }); }); // Simulate keeping the application running console.log('āœ… Client setup complete. Listening for real-time updates...'); // Clean up after 30 seconds (in real apps, this would be on app shutdown) setTimeout(() => { unsubscribe(); configClient.disconnect(); console.log('šŸ”Œ Client disconnected'); }, 30000); } catch (error) { console.error('āŒ Error:', error); } } // Example 2: Advanced Usage with Multiple Configurations async function advancedExample() { console.log('\n=== Advanced Dynamic Config Example ==='); const config: DynamicConfigClientConfig = { apiKey: process.env.VARIABLY_API_KEY || 'your-api-key', jwtToken: process.env.VARIABLY_JWT_TOKEN || 'your-jwt-token', projectId: process.env.VARIABLY_PROJECT_ID || 'your-project-id', baseUrl: process.env.VARIABLY_BASE_URL || 'https://graphql.variably.tech', enableRealtime: true, pollingInterval: 60000, // 60 second fallback polling cache: { ttl: 300000, // 5 minute cache maxSize: 500, enabled: true }, debug: true }; const configClient = new DynamicConfigClient(config); const userContext: UserContext = { userId: 'advanced-user-456', email: 'advanced@example.com', country: 'CA', platform: 'web', version: '2.1.0', attributes: { subscription: 'enterprise', feature_flags: ['beta', 'experimental'] } }; // Configuration keys we're interested in const configKeys = [ 'feature-limits', 'pricing-model', 'ui-customization', 'integration-settings', 'notification-preferences' ]; try { // Get multiple configurations console.log('šŸ“‹ Loading configurations...'); const configurations = await Promise.all( configKeys.map(async (key) => { const result = await configClient.evaluateConfig(key, {}, userContext); return { key, result }; }) ); configurations.forEach(({ key, result }) => { console.log(` āœ“ ${key}:`, { value: result.value, reason: result.reason, cached: result.cacheHit, version: result.version }); }); // Subscribe to all configuration changes const unsubscribeAll = configClient.onAnyConfigChange((result) => { console.log('🌐 Any config changed:', { key: result.key, value: result.value, updateType: result.realTimeUpdate ? 'real-time' : 'polling', version: result.version, timestamp: result.retrievedAt }); // Handle specific configuration changes switch (result.key) { case 'feature-limits': console.log(' šŸ”§ Updating feature limits in application...'); break; case 'ui-customization': console.log(' šŸŽØ Applying new UI customization...'); break; case 'pricing-model': console.log(' šŸ’° Updating pricing display...'); break; } }); // Monitor connection status const connectionStatus = configClient.getConnectionStatus(); console.log('šŸ”— Connection status:', connectionStatus); // Refresh configurations manually if needed setTimeout(async () => { console.log('šŸ”„ Manually refreshing configurations...'); await configClient.refreshConfigs(userContext); }, 10000); // Clean up setTimeout(() => { unsubscribeAll(); configClient.disconnect(); console.log('šŸ”Œ Advanced client disconnected'); }, 45000); } catch (error) { console.error('āŒ Advanced example error:', error); } } // Example 3: Polling-Only Mode (for environments without WebSocket support) async function pollingOnlyExample() { console.log('\n=== Polling-Only Mode Example ==='); const config: DynamicConfigClientConfig = { apiKey: 'your-api-key', projectId: 'your-project-id', baseUrl: 'https://graphql.variably.tech', enableRealtime: false, // Disable real-time updates pollingInterval: 30000, // 30 second polling debug: true }; const configClient = new DynamicConfigClient(config); const userContext: UserContext = { userId: 'polling-user-789', attributes: { environment: 'restricted-network' } }; try { // Get configuration (will use polling for updates) const maintenanceMode = await configClient.getConfigBool( 'maintenance-mode', false, userContext ); console.log('Maintenance mode:', maintenanceMode); // Subscribe to changes (will be detected via polling) const unsubscribe = configClient.onConfigChange('maintenance-mode', (result) => { console.log('šŸ“Š Polling detected change:', { key: result.key, value: result.value, reason: result.reason }); }); console.log('āœ… Polling-only client active. Checking for updates every 30 seconds...'); // Clean up setTimeout(() => { unsubscribe(); configClient.disconnect(); console.log('šŸ”Œ Polling client disconnected'); }, 120000); } catch (error) { console.error('āŒ Polling example error:', error); } } // Example 4: Error Handling and Resilience async function errorHandlingExample() { console.log('\n=== Error Handling Example ==='); const config: DynamicConfigClientConfig = { apiKey: 'invalid-api-key', // Intentionally invalid jwtToken: 'invalid-jwt-token', projectId: 'test-project', baseUrl: 'https://graphql.variably.tech', enableRealtime: true, debug: true }; const configClient = new DynamicConfigClient(config); const userContext: UserContext = { userId: 'error-test-user' }; try { // This will fail due to invalid API key, but should return default value const result = await configClient.evaluateConfig( 'test-config', 'default-value', userContext ); console.log('Result with error handling:', { value: result.value, reason: result.reason, hasError: !!result.error, errorMessage: result.error?.message }); // The client should fall back to polling mode if WebSocket fails const status = configClient.getConnectionStatus(); console.log('Connection status after error:', status); } catch (error) { console.error('āŒ Unexpected error (should be handled gracefully):', error); } configClient.disconnect(); } // Run examples async function runExamples() { console.log('šŸš€ Starting Variably Dynamic Config SDK Examples\n'); // Run examples in sequence await basicRealtimeExample(); await new Promise(resolve => setTimeout(resolve, 2000)); await advancedExample(); await new Promise(resolve => setTimeout(resolve, 2000)); await pollingOnlyExample(); await new Promise(resolve => setTimeout(resolve, 2000)); await errorHandlingExample(); console.log('\nāœ… All examples completed!'); } // Export examples for use in documentation or testing export { basicRealtimeExample, advancedExample, pollingOnlyExample, errorHandlingExample, runExamples }; // Run if this file is executed directly if (require.main === module) { runExamples().catch(console.error); }