Skip to main content

Standalone @flaggr/sdk package with pluggable OTEL instrumentation

TypeScript SDK (@flaggr/sdk)

The standalone Flaggr SDK for TypeScript. Includes a core client, React hooks, and pluggable OpenTelemetry instrumentation.

Installation

npm install @flaggr/sdk

Quick Start

import { createFlaggr } from '@flaggr/sdk'
 
const client = createFlaggr({
  serviceId: 'web-app',
  apiKey: 'flg_your_token',
})
 
const isEnabled = await client.getBooleanValue('checkout-v2', false)

Core Client API

Flag Evaluation Methods

// Boolean flag
const enabled: boolean = await client.getBooleanValue('my-flag', false)
 
// String flag
const variant: string = await client.getStringValue('button-color', 'blue')
 
// Number flag
const limit: number = await client.getNumberValue('rate-limit', 100)
 
// Object flag (typed)
interface BannerConfig {
  text: string
  color: string
  dismissible: boolean
}
const banner: BannerConfig = await client.getObjectValue<BannerConfig>(
  'banner-config',
  { text: '', color: 'blue', dismissible: true }
)

Evaluation with Context

Pass targeting context for per-user or per-segment evaluation:

const result = await client.getBooleanValue('checkout-v2', false, {
  targetingKey: 'user-456',
  email: 'bob@example.com',
  plan: 'enterprise',
  country: 'AU',
})

Detailed Evaluation

When you need the full evaluation result (reason, variant, metadata):

const detail = await client.getBooleanDetails('checkout-v2', false)
 
// detail.value       → true
// detail.reason      → "TARGETING_MATCH"
// detail.variant     → "enabled"
// detail.flagKey     → "checkout-v2"
// detail.errorCode   → undefined (present on errors)

With React

import { FlaggrProvider, useBooleanFlag } from '@flaggr/sdk/react'
 
function App() {
  return (
    <FlaggrProvider config={{
      serviceId: 'web-app',
      apiKey: 'flg_your_token',
    }}>
      <Checkout />
    </FlaggrProvider>
  )
}
 
function Checkout() {
  const useNewFlow = useBooleanFlag('checkout-v2', false)
  return useNewFlow ? <CheckoutV2 /> : <CheckoutClassic />
}

Available Hooks

HookReturnsDescription
useBooleanFlag(key, default)booleanBoolean flag value
useStringFlag(key, default)stringString flag value
useNumberFlag(key, default)numberNumber flag value
useObjectFlag<T>(key, default)TTyped object flag value
useConnectionState()ConnectionStateCurrent connection state
useRefreshFlags()() => Promise<void>Manual refresh function

Hook Behavior

All flag hooks subscribe to real-time updates when streaming is enabled. When a flag value changes server-side, the hook re-renders the component automatically. If the SDK is disconnected, hooks return the default value you provided.

// ConnectionState is one of:
type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'error'
 
// Use in a status indicator
function ConnectionBadge() {
  const state = useConnectionState()
  return (
    <span className={state === 'connected' ? 'text-green-500' : 'text-red-500'}>
      {state}
    </span>
  )
}

Error Handling

The SDK is designed to fail safely. Evaluation methods never throw -- they return the default value and report errors via the plugin system.

// This never throws, even if the server is unreachable
const value = await client.getBooleanValue('my-flag', false)
// → false (default) when an error occurs

Error Codes

CodeMeaningRecovery
FLAG_NOT_FOUNDFlag key does not existCheck the key spelling and service ID
PARSE_ERRORFlag value type mismatchVerify you are calling the right method (boolean/string/number)
PROVIDER_NOT_READYSDK not yet initializedWait for connected state or use await initializeFlaggr()
GENERALNetwork/server errorSDK retries automatically; check connectivity
TARGETING_KEY_MISSINGContext lacks targetingKeyAdd targetingKey to evaluation context

Custom Error Handling

Use the plugin system for logging and alerting on errors:

const client = createFlaggr({
  serviceId: 'web-app',
  apiKey: 'flg_your_token',
  plugins: [{
    name: 'error-reporter',
    onEvaluateError(flagKey, error, durationMs) {
      console.error(`Flag evaluation failed: ${flagKey}`, error)
      // Send to your error tracking service
      Sentry.captureException(error, {
        tags: { flagKey, durationMs },
      })
    },
  }],
})

Retry and Reconnection

The SDK handles transient failures automatically with exponential backoff:

const client = createFlaggr({
  serviceId: 'web-app',
  apiKey: 'flg_your_token',
  // Retry configuration (these are the defaults)
  retryConfig: {
    maxRetries: 3,           // Max retry attempts per evaluation
    initialDelayMs: 100,     // First retry delay
    maxDelayMs: 5000,        // Maximum backoff delay
    backoffMultiplier: 2,    // Exponential backoff factor
  },
  // Streaming reconnection
  enableStreaming: true,
  streamReconnectDelayMs: 1000,  // Initial reconnect delay for SSE/gRPC streams
})

When streaming is enabled, the SDK reconnects automatically on connection loss. The reconnection delay doubles on each attempt (up to 30 seconds) and resets on successful connection.

With OpenTelemetry

The SDK supports pluggable OTEL instrumentation. Install the peer dependency:

npm install @opentelemetry/api

Basic OTEL Setup

import { createFlaggr } from '@flaggr/sdk'
import { otelPlugin } from '@flaggr/sdk/otel'
 
const client = createFlaggr({
  serviceId: 'web-app',
  apiKey: 'flg_your_token',
  plugins: [otelPlugin()],  // Uses global OTEL providers
})

Custom Configuration

import { otelPlugin } from '@flaggr/sdk/otel'
 
plugins: [otelPlugin({
  serviceName: 'checkout-service',
  enableTracing: true,
  enableMetrics: true,
  metricPrefix: 'myapp.flags',
  additionalAttributes: {
    'deployment.environment': 'production',
    'service.version': '1.2.3',
  },
})]

Bring Your Own Providers

import { MeterProvider } from '@opentelemetry/sdk-metrics'
import { otelPlugin } from '@flaggr/sdk/otel'
 
const meterProvider = new MeterProvider({
  readers: [myMetricReader],
})
 
plugins: [otelPlugin({
  meterProvider,
  tracerProvider: myTracerProvider,
})]

Exported Metrics

MetricTypeDescription
flaggr.evaluations.totalCounterTotal flag evaluations
flaggr.evaluations.durationHistogramEvaluation latency (ms)
flaggr.evaluations.errorsCounterEvaluation errors
flaggr.cache.hitsCounterCache hits
flaggr.cache.missesCounterCache misses
flaggr.flag_changes.totalCounterFlag change events
flaggr.connections.activeUpDownCounterActive connections

Exported Spans

Each flag evaluation creates a span named flaggr.evaluate with attributes:

AttributeDescription
feature_flag.keyFlag key being evaluated
feature_flag.provider_nameAlways "flaggr"
feature_flag.valueEvaluated value
feature_flag.reasonEvaluation reason
feature_flag.variantVariant key (if applicable)

Plugin System

The SDK supports a plugin architecture for extending behavior:

import type { FlaggrPlugin } from '@flaggr/sdk'
 
const myPlugin: FlaggrPlugin = {
  name: 'my-plugin',
  onInit(client) { /* SDK initialized */ },
  onEvaluate(flagKey, context) { /* Before evaluation */ },
  onEvaluateComplete(flagKey, result, durationMs) { /* After evaluation */ },
  onEvaluateError(flagKey, error, durationMs) { /* On error */ },
  onFlagChange(event) { /* Flag value changed */ },
  onConnectionStateChange(state) { /* Connection state changed */ },
  onDestroy() { /* SDK destroyed */ },
}
 
const client = createFlaggr({
  serviceId: 'web-app',
  plugins: [myPlugin],
})

Plugin Lifecycle

createFlaggr()
  ├─ onInit()           Called once after client creation
  │
  ├─ evaluate('flag')
  │   ├─ onEvaluate()          Before evaluation
  │   ├─ onEvaluateComplete()  After successful evaluation
  │   └─ onEvaluateError()     On evaluation failure
  │
  ├─ onFlagChange()     When a flag value changes (streaming only)
  ├─ onConnectionStateChange()  On connection state transitions
  │
  └─ client.destroy()
      └─ onDestroy()    Cleanup (close connections, flush metrics)

Configuration

interface FlaggrConfig {
  apiUrl?: string        // Default: 'https://flaggr.dev'
  serviceId: string      // Your service identifier
  apiKey?: string        // API token
  environment?: string   // Target environment
  context?: EvaluationContext  // Default evaluation context
  plugins?: FlaggrPlugin[]     // SDK plugins
  cacheTtl?: number      // Cache TTL in ms (default: 10000)
  enableStreaming?: boolean  // SSE real-time updates
  defaults?: Record<string, FlagValue>  // Offline defaults
  retryConfig?: RetryConfig  // Retry configuration
  streamReconnectDelayMs?: number  // Initial stream reconnect delay
}

Offline Defaults

Provide fallback values for when the SDK cannot reach the server:

const client = createFlaggr({
  serviceId: 'web-app',
  apiKey: 'flg_your_token',
  defaults: {
    'checkout-v2': false,
    'dark-mode': true,
    'rate-limit': 100,
    'banner-config': { text: 'Welcome', color: 'blue', dismissible: true },
  },
})

When the server is unreachable and the local cache is empty, the SDK returns values from defaults instead of the method-level default. This lets you centralize your fallback configuration.