Skip to main content

Standalone @flaggr/sdk package with pluggable OTEL instrumentation

Last updated May 31, 2026

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: 'fgr_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.evaluate<boolean>('checkout-v2', false)
 
// detail.value       → true
// detail.reason      → "TARGETING_MATCH"
// detail.variant     → "enabled"
// detail.errorMessage → undefined (present on errors)

With React

import { FlaggrProvider, useBooleanFlag } from '@flaggr/sdk/react'
 
function App() {
  return (
    <FlaggrProvider config={{
      serviceId: 'web-app',
      apiKey: 'fgr_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

Evaluation Reasons and Errors

ReasonMeaningRecovery
STATICFlag returned its configured default or static valueNo action needed
DEFAULTSDK returned a configured offline/default valueCheck server reachability if unexpected
TARGETING_MATCHTargeting rules matched the supplied contextNo action needed
SPLITRollout or variant allocation selected this valueNo action needed
DISABLEDFlag is disabledEnable the flag or update the fallback
NOT_FOUNDFlag key does not existCheck the key spelling and service ID
ERRORNetwork/server error or invalid responseCheck connectivity and errorMessage

Custom Error Handling

Use the plugin system for logging and alerting on errors:

const client = createFlaggr({
  serviceId: 'web-app',
  apiKey: 'fgr_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 },
      })
    },
  }],
})

Streaming Reconnection

Evaluation methods fail safely by returning the provided default value and reporting errors through plugins. The SDK does not expose per-evaluation retry configuration. When streaming is enabled, browser EventSource reconnects automatically after connection loss:

const client = createFlaggr({
  serviceId: 'web-app',
  apiKey: 'fgr_your_token',
  enableStreaming: true,
})

When streaming is enabled, the SDK updates its connection state to error on stream errors and relies on the browser's EventSource reconnection behavior.

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: 'fgr_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://api.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
}

Offline Defaults

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

const client = createFlaggr({
  serviceId: 'web-app',
  apiKey: 'fgr_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.