SDK Reference

TypeScript / Node.js

Universal TypeScript SDK. Works anywhere JavaScript runs — Node.js, browsers, NestJS, edge runtimes (Cloudflare Workers, Vercel Edge, Deno), React Native, and Electron.

Installation

bash
npm install toggleai-sdk

Basic Usage

The TypeScript SDK operates in close synergy with ToggleAI backend services to guarantee high performance, security, and developer convenience. Evaluated values are resolved using two standard paradigms under the hood:

⚡ Local In-Memory Evaluation

When client.init() is called, the SDK makes a single, secure HTTP request to GET /sdk/config, retrieving the active configuration rules. Subsequent evaluations like client.getFlag() execute in-memory with sub-millisecond latency—eliminating all network hops from critical user paths.

🌐 Real-Time Remote Evaluation

In latency-tolerant or highly sensitive contexts requiring immediate synchronization, client.evaluateFlagRemote() makes a direct edge call to POST /sdk/evaluate, completely bypassing local caches to fetch the absolute latest resolution logic.

app.ts
import { ToggleAIClient } from "toggleai-sdk";

const client = new ToggleAIClient({
  clientId: process.env.TOGGLEAI_CLIENT_ID!,
  secret: process.env.TOGGLEAI_SECRET!,
  pollingInterval: 30000,
  defaultContext: {
    attributes: {
      server_region: "us-east-1",
      app_version: "2.5.0",
    },
  },
  onReady: () => console.log("ToggleAI ready!"),
  onConfigUpdate: (payload) => console.log("Refreshed at", payload.generatedAt),
  onError: (error) => console.error("SDK Error:", error.code, error.message),
});

await client.init();

// Feature flags — evaluated instantly in-memory from local cache
const ctx = { userId: "user_123", attributes: { plan: "premium" } };
if (client.getFlag("new-checkout", ctx)) {
  showNewCheckout();
}

// Server-side evaluation — makes direct API call bypassing local cache
const remoteResult = await client.evaluateFlagRemote("new-checkout", ctx);

// Remote config — resolved instantly locally
const timeout = client.getConfig<number>("api_timeout_ms", 5000);

// Logging — non-blocking, batched, and asynchronously flushed
const logger = client.getLogger();
logger.info("Request processed", { userId: "user_123" });

// Cleanup — flushes final logs and stops the background polling loop
client.close();

NestJS Integration

The SDK ships with a first-class NestJS module at toggleai-sdk/nestjs.

Static Configuration

app.module.ts
import { Module } from "@nestjs/common";
import { ToggleAIModule } from "toggleai-sdk/nestjs";

@Module({
  imports: [
    ToggleAIModule.forRoot({
      clientId: "pk_live_xxx",
      secret: "sk_live_xxx",
    }),
  ],
})
export class AppModule {}

Async Configuration (ConfigService)

app.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { ToggleAIModule } from "toggleai-sdk/nestjs";

@Module({
  imports: [
    ConfigModule.forRoot(),
    ToggleAIModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        clientId: config.getOrThrow("TOGGLEAI_CLIENT_ID"),
        secret: config.getOrThrow("TOGGLEAI_SECRET"),
      }),
    }),
  ],
})
export class AppModule {}

Injecting the Client

feature.service.ts
import { Injectable } from "@nestjs/common";
import { InjectToggleAI, ToggleAIClient } from "toggleai-sdk/nestjs";

@Injectable()
export class FeatureService {
  constructor(@InjectToggleAI() private readonly client: ToggleAIClient) {}

  isDarkMode(userId: string): boolean {
    return this.client.getFlag("dark-mode", { userId });
  }

  getApiTimeout(): number {
    return this.client.getConfig<number>("api_timeout_ms", 5000);
  }

  async evaluateRemote(flagKey: string, userId: string) {
    return this.client.evaluateFlagRemote(flagKey, { userId });
  }
}

React Native Setup

The SDK works with React Native out of the box since it uses isomorphic fetch. No native modules required.

toggleai.ts
// lib/toggleai.ts — Singleton client
import { ToggleAIClient } from "toggleai-sdk";

export const toggleai = new ToggleAIClient({
  clientId: "pk_live_xxx",
  secret: "sk_live_xxx",
  pollingInterval: 60000, // poll less frequently on mobile
});
App.tsx
// App.tsx — Initialize on app start
import { useEffect, useState } from "react";
import { toggleai } from "./lib/toggleai";

export default function App() {
  const [ready, setReady] = useState(false);

  useEffect(() => {
    toggleai.init().then(() => setReady(true));
    return () => toggleai.close();
  }, []);

  if (!ready) return <LoadingScreen />;

  return <MainNavigator />;
}

// In any component:
function CheckoutScreen() {
  const showNew = toggleai.getFlag("new-checkout", {
    userId: currentUser.id,
    attributes: { platform: "ios" },
  });

  return showNew ? <NewCheckout /> : <LegacyCheckout />;
}

Electron Setup

For Electron apps, initialize the SDK in the main process and expose flag values to renderer processes via IPC or preload scripts.

main.ts
// Main process
import { ToggleAIClient } from "toggleai-sdk";
import { ipcMain } from "electron";

const client = new ToggleAIClient({
  clientId: process.env.TOGGLEAI_CLIENT_ID!,
  secret: process.env.TOGGLEAI_SECRET!,
});

app.whenReady().then(async () => {
  await client.init();

  // Expose flag evaluation to renderer via IPC
  ipcMain.handle("toggleai:getFlag", (_event, key: string, ctx?: any) => {
    return client.getFlag(key, ctx);
  });

  ipcMain.handle("toggleai:getConfig", (_event, key: string, def: any) => {
    return client.getConfig(key, def);
  });
});

app.on("before-quit", () => client.close());
preload.ts
// Preload script
import { contextBridge, ipcRenderer } from "electron";

contextBridge.exposeInMainWorld("toggleai", {
  getFlag: (key: string, ctx?: any) =>
    ipcRenderer.invoke("toggleai:getFlag", key, ctx),
  getConfig: (key: string, defaultValue: any) =>
    ipcRenderer.invoke("toggleai:getConfig", key, defaultValue),
});

Error Handling

typescript
import { ToggleAIError } from "toggleai-sdk";

try {
  await client.init();
} catch (error) {
  if (error instanceof ToggleAIError) {
    switch (error.code) {
      case "INVALID_KEY":   console.error("Bad credentials"); break;
      case "RATE_LIMITED":  console.error("Rate limited"); break;
      case "FORBIDDEN":     console.error("Insufficient scope"); break;
      case "NETWORK_ERROR": console.error("Cannot reach API"); break;
    }
  }
}

Event Listeners

OptionTypeDescription
onReady() => voidClient initialized and config fetched
onConfigUpdate(payload) => voidConfig payload refreshed by polling
onError(error) => voidSDK error (network, rate limit, etc.)

API Reference

Client Lifecycle

MethodReturnsDescription
init()Promise<void>Fetch config, start polling
close()voidStop polling, flush logs
refresh()Promise<void>Manually refresh config
isReady()booleanClient initialized?
waitForReady()Promise<void>Wait for initialization
getState()ClientStateCurrent lifecycle state

Feature Flags

MethodReturns
getFlag(key, ctx?, default?)boolean
getFlagValue<T>(key, ctx?, default?)T
evaluateFlag(key, ctx?)FlagEvaluationResult
evaluateAllFlags(ctx?)Record<string, FlagEvaluationResult>
evaluateFlagRemote(key, ctx?)Promise<FlagEvaluationResult>
evaluateAllFlagsRemote(ctx?)Promise<Record<...>>

Remote Config

MethodReturns
getConfig<T>(key, default?)T
getAllConfigs()Record<string, unknown>
hasConfig(key)boolean
getConfigKeys()string[]

Logging

MethodReturns
getLogger()ToggleAILogger
logger.info(msg, ctx?)void
logger.error(err, ctx?)void
logger.captureError(err, ctx?)void
logger.setContext(ctx)void
logger.flush()Promise<void>