JavaScript (Official SDK)
Seamless integration with Vanilla JavaScript streamlines the testing and development of features across production and various environments. The Basestack JavaScript SDK is a lightweight, framework-agnostic library that works in any JavaScript environment, browser, Node.js, or serverless functions.
This SDK provides a simple, promise-based API for fetching and managing feature flags with built-in caching, error handling, and TypeScript support. Whether you're building with Vue, Svelte, Angular, or plain JavaScript, this SDK integrates effortlessly into your workflow.
Getting started
Examples
Install
Install the SDK using your preferred package manager. The SDK is lightweight and has no external dependencies beyond the Fetch API (available in all modern browsers and Node.js 18+).
npm install @basestack/flags-jspnpm install @basestack/flags-jsyarn add @basestack/flags-jsbun add @basestack/flags-jsAlternative: Script Tag
For quick prototyping or when you can't use a bundler, you can include the SDK via a script tag:
<script type="module">
import { FlagsSDK } from "https://unpkg.com/@basestack/flags-js";
// Use FlagsSDK here
</script>Environment Variables
Configure your environment variables to connect to your Basestack Feature Flags project. The variable names can be customized to match your project's conventions.
When it comes to environment variables, pay attention to the framework you're
using. For example, NEXT_PUBLIC_ is specific to Next.js, while in
Vite.js, it would be VITE_. Example
VITE_FEATURE_FLAGS_BASE_URL= or FEATURE_FLAGS_BASE_URL=
# BASESTACK FEATURE FLAGS
FEATURE_FLAGS_BASE_URL="https://flags.basestack.co/api/v1"
FEATURE_FLAGS_PROJECT_KEY=""
FEATURE_FLAGS_ENVIRONMENT_KEY=""You can find your project and environment keys in your Basestack Feature Flags Dashboard.
Make sure to add your .env file to .gitignore to keep your keys secure. Never commit sensitive credentials to version control.
Create SDK Instance
Create a new instance of the FlagsSDK class with your configuration. The SDK instance manages all flag operations, caching, and API communication.
Basic Initialization
For most use cases, a simple configuration is sufficient:
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
baseURL: process.env.FEATURE_FLAGS_BASE_URL,
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.FEATURE_FLAGS_ENVIRONMENT_KEY,
});Advanced Configuration
For production applications, you may want to customize caching behavior and preload specific flags:
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
baseURL: process.env.FEATURE_FLAGS_BASE_URL,
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.FEATURE_FLAGS_ENVIRONMENT_KEY,
// Preload specific flags on initialization
preloadFlags: ["header", "footer", "checkout"],
// Customize cache settings
cache: {
enabled: true,
ttl: 10 * 60 * 1000, // 10 minutes (default: 5 minutes)
maxSize: 50, // Maximum cached flags (default: 100)
},
});Key Configuration Options:
baseURL: Your Basestack API endpoint (optional, defaults to production URL)projectKey: Your project identifier (required)environmentKey: Your environment identifier (required)preloadFlags: Array of flag slugs to fetch immediately (optional)cache: Cache configuration object (optional)
Initialize the SDK
Before using the SDK, you should call init() to preload flags and enable caching. This step is optional but recommended for better performance.
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.FEATURE_FLAGS_ENVIRONMENT_KEY,
});
// Initialize the SDK (optional but recommended)
// This preloads flags and enables caching
await client.init();When to use init():
- ✅ When you want to preload flags on app startup
- ✅ When you want to enable caching for better performance
- ✅ When you've configured
preloadFlagsin your config
When you can skip init():
- ✅ When fetching flags on-demand is sufficient
- ✅ When you don't need caching
- ✅ Flags will still work, but won't be cached until first fetch
Fetch Flags
Now you can fetch flags using the SDK methods. The SDK provides two main methods: getFlag() for a single flag and getAllFlags() for all flags.
Fetching a Single Flag
Use getFlag() to retrieve a specific flag by its slug:
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
projectKey: "your-project-key",
environmentKey: "your-environment-key",
});
async function checkFeatureFlag() {
try {
const headerFlag = await client.getFlag("header");
if (headerFlag.enabled) {
console.log("Header feature is enabled");
console.log("Payload:", headerFlag.payload);
// Use the flag to enable features
enableNewHeader(headerFlag.payload);
} else {
console.log("Header feature is disabled");
// Use default behavior
useDefaultHeader();
}
} catch (error) {
console.error("Failed to fetch flag:", error);
// Fallback to default behavior
useDefaultHeader();
}
}
// Call the function
await checkFeatureFlag();Fetching All Flags
Use getAllFlags() to retrieve all flags for your environment:
async function listAllFlags() {
try {
const { flags } = await client.getAllFlags();
console.log(`Found ${flags.length} flag(s)`);
flags.forEach((flag) => {
console.log(`Flag: ${flag.slug}`);
console.log(`Enabled: ${flag.enabled}`);
console.log(`Description: ${flag.description}`);
console.log(`Payload:`, flag.payload);
});
} catch (error) {
console.error("Failed to fetch flags:", error);
}
}
await listAllFlags();Flag Object Structure
Each flag object contains:
slug: Unique identifier for the flagenabled: Boolean indicating if the flag is enabledpayload: Optional data associated with the flagdescription: Human-readable descriptioncreatedAt: Date when the flag was createdupdatedAt: Date when the flag was last updatedexpiredAt: Optional expiration date
Common Usage Patterns
Conditional Feature Rendering
Use flags to conditionally render features in your application:
async function renderHeader() {
try {
const headerFlag = await client.getFlag("new-header-design");
if (headerFlag.enabled) {
renderNewHeader(headerFlag.payload);
} else {
renderDefaultHeader();
}
} catch (error) {
console.error("Error loading header flag:", error);
// Always fallback to default
renderDefaultHeader();
}
}Cache Management
The SDK includes built-in caching to reduce API calls. Manage the cache as needed:
// Clear entire cache
client.clearCache();
// Clear cache for a specific flag
client.clearFlagCache("header");
// Flags will be fetched fresh on next request
const freshFlag = await client.getFlag("header");Advanced Usage
Custom Fetch Implementation
For environments without native Fetch API (Node.js < 18), provide a custom fetch implementation:
import fetch from "node-fetch";
import { FlagsSDK } from "@basestack/flags-js";
const client = new FlagsSDK({
projectKey: "your-project-key",
environmentKey: "your-environment-key",
fetchImpl: fetch, // Use node-fetch or other implementation
});Error Handling Best Practices
Always handle errors gracefully:
async function getFlagSafely(slug, defaultValue = false) {
try {
const flag = await client.getFlag(slug);
return flag.enabled;
} catch (error) {
console.error(`Failed to fetch flag "${slug}":`, error);
// Return safe default value
return defaultValue;
}
}
// Usage with fallback
const isEnabled = await getFlagSafely("new-feature", false);TypeScript Usage
The SDK is built with TypeScript and provides full type safety:
import { FlagsSDK, type Flag, type SDKConfig } from "@basestack/flags-js";
const config: SDKConfig = {
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY!,
environmentKey: process.env.FEATURE_FLAGS_ENVIRONMENT_KEY!,
};
const client = new FlagsSDK(config);
// Type-safe flag fetching
async function getTypedFlag(): Promise<Flag> {
return await client.getFlag("header");
}
// Type-safe payload
interface HeaderPayload {
variant: "light" | "dark";
showLogo: boolean;
}
async function getHeaderConfig() {
const flag = await client.getFlag("header");
const payload = flag.payload as HeaderPayload;
return payload;
}Best Practices
1. Initialize Early
Initialize the SDK as early as possible in your application lifecycle:
// In your app initialization
const client = new FlagsSDK(config);
await client.init(); // Preload flags before rendering2. Use Caching
Enable caching to reduce API calls and improve performance:
const client = new FlagsSDK({
// ... other config
cache: {
enabled: true,
ttl: 5 * 60 * 1000, // 5 minutes
},
});3. Handle Errors Gracefully
Always provide fallback behavior when flags fail to load:
async function getFeatureState(flagSlug, fallback = false) {
try {
const flag = await client.getFlag(flagSlug);
return flag.enabled;
} catch (error) {
console.error(`Flag "${flagSlug}" failed:`, error);
return fallback;
}
}4. Preload Critical Flags
Preload flags that are needed immediately:
const client = new FlagsSDK({
// ... other config
preloadFlags: ["header", "footer", "checkout"],
});5. Use Environment-Specific Configs
Use different configurations for different environments:
const isProduction = process.env.NODE_ENV === "production";
const client = new FlagsSDK({
baseURL: isProduction
? "https://flags.basestack.co/api/v1"
: "https://flags-dev.basestack.co/api/v1",
projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY,
environmentKey: isProduction
? process.env.PROD_ENVIRONMENT_KEY
: process.env.DEV_ENVIRONMENT_KEY,
});Troubleshooting
Flags Not Loading
If flags aren't loading:
- Check Configuration: Verify your
projectKeyandenvironmentKeyare correct - Check Network: Ensure your application can reach the Basestack API endpoint
- Check Console: Look for error messages in the browser console or server logs
- Verify Environment Variables: Ensure environment variables are set correctly
Cache Issues
If flags aren't updating:
- Clear Cache: Use
client.clearCache()to force fresh fetches - Check TTL: Verify your cache TTL isn't too long
- Manual Refresh: Fetch flags manually to bypass cache
TypeScript Errors
If you're getting TypeScript errors:
- Install Types: Types are included with the package, no additional installation needed
- Check Imports: Use
import typefor type-only imports - Type Assertions: Use type assertions for payloads when needed
Fetch API Not Available
If Fetch API isn't available (Node.js < 18):
- Install node-fetch:
npm install node-fetch - Provide Custom Fetch: Pass
fetchImploption to SDK config - Use Polyfill: Use a Fetch polyfill for older environments
TypeScript Support
The SDK is built with TypeScript and provides full type definitions. All types are exported for your use:
import type {
FlagsSDK,
Flag,
SDKConfig,
CacheConfig,
} from "@basestack/flags-js";
// Use types in your code
const config: SDKConfig = {
projectKey: "your-key",
environmentKey: "your-env-key",
};
const client = new FlagsSDK(config);
const flag: Flag = await client.getFlag("header");Type Definitions
The SDK exports the following TypeScript interfaces:
/** Configuration for caching mechanism */
export interface CacheConfig {
/** Enable or disable caching (default: true) */
enabled?: boolean;
/** Time-to-Live for cache entries in milliseconds (default: 5 minutes) */
ttl?: number;
/** Maximum number of cache entries (default: 100) */
maxSize?: number;
}
/** SDK Configuration Options */
export interface SDKConfig {
/** Base URL for the feature flag service (optional) */
baseURL?: string;
/** Project identification key (required) */
projectKey: string;
/** Environment identification key (required) */
environmentKey: string;
/** Caching configuration options */
cache?: CacheConfig;
/** Custom fetch implementation (optional) */
fetchImpl?: typeof fetch;
/** Flags to preload during initialization */
preloadFlags?: string[];
}
/** Feature flag data structure */
export interface Flag {
slug: string;
enabled: boolean;
payload?: unknown;
expiredAt?: Date | null;
createdAt: Date;
updatedAt: Date;
description: string;
error?: boolean;
}SDK Reference
Initialization Options
| Property | Type | Description | Required | Default Value |
|---|---|---|---|---|
baseURL | string | Specifies the target URL to point to. | false | https://flags.basestack.co/api/v1 |
projectKey | string | Specifies the project to retrieve data from. | true | null |
environmentKey | string | Specifies the project environment from which to retrieve flags. | true | null |
preloadFlags | string[] | List of flags to preload during initialization. | false | [] |
cache.enabled | boolean | Enables or disables caching of flags. | false | true |
cache.ttl | number | Time-to-live for cached flags in milliseconds. | false | 5 * 60 * 1000 |
cache.maxSize | number | Maximum number of flags to store in the cache. | false | 100 |
fetchImpl | function | Custom fetch implementation (for Node.js < 18). | false | global.fetch |
Available Methods
| Method | Description | Returns | Environment |
|---|---|---|---|
init() => Promise<void> | Initiates the flags request, enabling caching capabilities. | Promise<void> | Browser & Node |
clearCache(): void | Clears all cached flags. | void | Browser & Node |
clearFlagCache(slug: string): void | Clears the cache for a specific flag identified by the slug. | void | Browser & Node |
getFlag(slug: string): Promise<Flag> | Retrieves a specific flag by its slug. | Promise<Flag> | Browser & Node |
getAllFlags(): Promise<{ flags: Flag[] }> | Retrieves all flags. | Promise<{ flags: Flag[] }> | Browser & Node |
Method Details
init()
Preloads flags and enables caching. This is optional but recommended for better performance.
await client.init();getFlag(slug: string)
Fetches a single flag by its slug. Returns a Promise that resolves to a Flag object.
const flag = await client.getFlag("header");
console.log(flag.enabled); // true or false
console.log(flag.payload); // Optional payload datagetAllFlags()
Fetches all flags for the current environment. Returns a Promise that resolves to an object with a flags array.
const { flags } = await client.getAllFlags();
flags.forEach(flag => {
console.log(`${flag.slug}: ${flag.enabled}`);
});clearCache()
Clears all cached flags. Useful when you need to force fresh fetches.
client.clearCache();clearFlagCache(slug: string)
Clears the cache for a specific flag.
client.clearFlagCache("header");