Tggl Client SDK
The `tggl-client` package provides a TypeScript SDK for integrating Tggl feature flags into both client-side (browsers, React Native) and server-side (Node.js) applications. Currently at version 3.2.2, the library maintains an active release cadence with frequent updates and bug fixes, as evidenced by recent v3.x releases. Version 3 represents a complete rewrite focused on improved reliability, fault tolerance, and maintainability, introducing a simpler API, better error handling, and automatic HTTP retries via `ky`. A key differentiator is its enforcement of feature flag best practices, specifically by removing the `isActive` method and making the default value a required argument for the `get` method since v2.0.0, guiding developers to always handle the absence of a flag gracefully. It also offers built-in storage options for Postgres, Redis, and Static storage for server-side usage, requiring corresponding peer dependencies like `pg` or `redis` for these features. The SDK supports Node.js environments version 20.0.0 and above.
Common errors
-
TypeError: client.isActive is not a function
cause Attempting to call the `isActive` method, which was removed in `tggl-client` v2.0.0.fixUpdate your code to use the `get` method with a default boolean value instead, e.g., `client.get('feature-name', false)`. -
Argument of type 'undefined' is not assignable to parameter of type 'string | boolean | number | null | Record<string, any>'
cause Calling the `get` method without providing a required default value as the second argument (for client-side) or third argument (for server-side with context). This became mandatory in `tggl-client` v2.0.0.fixProvide a default value for the flag, e.g., `client.get('feature-name', true)` or `localClient.get(context, 'feature-name', 'default_value')`. -
ERR_REQUIRE_ESM
cause Attempting to import `tggl-client` using CommonJS `require()` syntax in an environment configured for ESM, or when the package's primary entry point is ESM-only.fixUse ESM `import` statements (e.g., `import { TgglClient } from 'tggl-client';`) and ensure your project is configured for ESM (e.g., `"type": "module"` in `package.json`). If running in Node.js, ensure your environment supports ESM. -
Error: Tggl client is not ready after Xms
cause The `waitReady()` method timed out, indicating an issue with fetching flags from the Tggl API (e.g., incorrect API key, network issues, or service unavailability).fixVerify your `apiKey` is correct and has the necessary permissions. Check network connectivity to `tggl.io` (or your configured `baseUrl`). Review Tggl's status page or logs for potential service outages or errors. Ensure firewalls are not blocking access.
Warnings
- breaking Version 2.0.0 introduced significant breaking changes: the `isActive` method was removed entirely, and the `get` method now requires a default value as a mandatory argument. This change enforces best practices, preventing code from relying on a flag's existence and instead promoting graceful fallbacks.
- breaking Version 3.0.0 was a complete rewrite, focusing on reliability and fault tolerance. While the core API (`get` method) remained largely consistent with v2, internal mechanisms changed (e.g., `ky` for HTTP requests), and API simplifications might affect advanced usage patterns or custom integrations. The SDK now requires Node.js >=20.0.0.
- gotcha It's crucial to distinguish between `TgglClient` (for client-side environments like browsers or React Native) and `TgglLocalClient` (for server-side Node.js applications). Using the wrong client type can lead to incorrect flag evaluations, performance issues, or security vulnerabilities (e.g., exposing server API keys on the client).
- gotcha The `waitReady()` method is asynchronous and must be awaited before attempting to retrieve flag values. If `get()` is called before the client is ready, it may return default values prematurely or encounter errors, as flags might not have been fetched yet.
Install
-
npm install tggl-client -
yarn add tggl-client -
pnpm add tggl-client
Imports
- TgglClient
import TgglClient from 'tggl-client'
import { TgglClient } from 'tggl-client' - TgglLocalClient
const { TgglLocalClient } = require('tggl-client')import { TgglLocalClient } from 'tggl-client' - StaticStorage
import { StaticStorage } from 'tggl-client'
Quickstart
import { TgglClient, TgglLocalClient, StaticStorage } from 'tggl-client';
async function runFeatureFlagExample() {
// It's highly recommended to use environment variables for API keys in production
const clientApiKey = process.env.TGGL_CLIENT_API_KEY ?? 'TGGL_CLIENT_KEY_XXX';
const serverApiKey = process.env.TGGL_SERVER_API_KEY ?? 'TGGL_SERVER_KEY_YYY';
// --- Client-side usage example (e.g., browser, React Native) ---
console.log("\n--- Initializing TgglClient (Client-side) ---");
const client = new TgglClient({
apiKey: clientApiKey,
initialContext: { userId: 'browser-user-123', plan: 'free' },
});
try {
await client.waitReady(); // Wait for initial flags to be fetched
if (client.get('enable-dark-mode', false)) {
console.log('Client: Dark mode is ENABLED for this user!');
} else {
console.log('Client: Dark mode is DISABLED or not found, using default.');
}
const welcomeMessage = client.get('welcome-banner-text', 'Hello there!');
console.log(`Client: Welcome message: "${welcomeMessage}"`);
} catch (error) {
console.error("Error with TgglClient initialization or flag evaluation:", error);
}
// --- Server-side usage example (e.g., Node.js backend) ---
console.log("\n--- Initializing TgglLocalClient (Server-side) ---");
// For production, consider PostgresStorage or RedisStorage with proper configuration
const storage = new StaticStorage();
const localClient = new TgglLocalClient({
apiKey: serverApiKey,
storage, // Pass the storage instance
refreshInterval: 60000, // Refresh flags from Tggl API every minute
});
try {
await localClient.waitReady(); // Wait for initial flags to be fetched
const serverContext = { userId: 'server-user-456', environment: 'production' };
if (localClient.get(serverContext, 'new-api-endpoint', false)) {
console.log('Server: Using new API endpoint for this context!');
} else {
console.log('Server: Using old API endpoint.');
}
const featureVariant = localClient.get({ userId: 'another-user-789' }, 'feature-x-variant', 'control');
console.log(`Server: 'feature-x-variant' for 'another-user-789' is: ${featureVariant}`);
} catch (error) {
console.error("Error with TgglLocalClient initialization or flag evaluation:", error);
} finally {
// Important: Stop background processes for local client when done (e.g., on server shutdown)
localClient.stopReporting?.();
localClient.stopRefreshing?.();
}
}
runFeatureFlagExample();