Basestack Docs
Frontend

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.

Why feature flags? They let you ship and test in production safely: use environments to hide or show work-in-progress features, roll out gradually, or turn a misbehaving feature off in real time without a full deploy. Learn more on the Feature Flags product page.

Getting started

We only support ESM (ECMAScript Modules) in the browser or server environment. If you are using CommonJS, you will need to use a bundler like Webpack or Rollup to bundle your code.

Examples

Example apps in the basestack-flags-js repository use the local package via link:../.. run a production build at the repo root (bun run build or your package manager’s build) before trying them.

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+).

Install the SDK
npm install @basestack/flags-js
pnpm install @basestack/flags-js
yarn add @basestack/flags-js
bun add @basestack/flags-js

Alternative: Script Tag

For quick prototyping or when you can't use a bundler, you can include the SDK via a script tag:

index.html
<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=

.env
# BASESTACK FEATURE FLAGS
FEATURE_FLAGS_BASE_URL="https://flags-api.basestack.co/v1"
FEATURE_FLAGS_PROJECT_KEY=""
FEATURE_FLAGS_ENVIRONMENT_KEY=""
# Optional β€” project API token for server-side helpers such as submitCodeReferences() only
# FEATURE_FLAGS_API_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:

src/flags.js
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:

src/flags.js
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)
  },

  // Optional β€” project API token from project settings; required for submitCodeReferences() and other authenticated writes (use server-side or CI only)
  // apiKey: process.env.FEATURE_FLAGS_API_KEY,
});

Key configuration options:

  • baseURL: Feature Flags API base URL (optional; defaults to https://flags-api.basestack.co/v1). For self-hosted Basestack, point this at your instance (for example https://your-domain.com/api/v1).
  • projectKey: Your project identifier (required)
  • environmentKey: Your environment identifier (required)
  • apiKey: Project API token for authenticated write methods such as submitCodeReferences() prefer server-side or CI; never expose in client bundles
  • preloadFlags: If set, init() preloads only these slugs; if omitted, init() preloads all flags
  • cache: Cache configuration object (optional)
  • fetchImpl: Custom fetch implementation (optional; useful for Node.js versions before 18 or test doubles)

Initialize the SDK

Call init() to preload flags into the internal cache. It is optional but recommended when you want flags ready before rendering or to warm the cache.

src/flags.js
import { FlagsSDK } from "@basestack/flags-js";

const client = new FlagsSDK({
  projectKey: process.env.FEATURE_FLAGS_PROJECT_KEY,
  environmentKey: process.env.FEATURE_FLAGS_ENVIRONMENT_KEY,
});

// Preload flags β€” if preloadFlags is set in config, only those load; if omitted, all flags load
await client.init();

Behavior:

  • If preloadFlags is set in config, init() loads only those slugs.
  • If preloadFlags is omitted, init() loads all flags for the project and environment.

When to use init():

  • βœ… You want flags (or a defined subset) loaded up front
  • βœ… You rely on caching for fewer round trips

When you can skip init():

  • βœ… You only call getFlag() / getAllFlags() on demand the first fetch still works; caching applies after data is loaded

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:

src/example.js
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:

src/example.js
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 flag
  • enabled: Boolean indicating if the flag is enabled
  • payload: Optional data associated with the flag
  • description: Human-readable description
  • createdAt: Date when the flag was created
  • updatedAt: Date when the flag was last updated
  • expiredAt: Optional expiration date

Common Usage Patterns

Conditional Feature Rendering

Use flags to conditionally render features in your application:

src/features.js
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:

src/cache.js
// 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");

Preview flags and feedback

getPreviewFlags()

Returns preview flags with rendered HTML content (Lexical-based previews in the dashboard).

src/preview.js
const previewResponse = await client.getPreviewFlags();

previewResponse.flags.forEach((previewFlag) => {
  console.log(previewFlag.slug);
  console.log(previewFlag.previewName);
  console.log(previewFlag.previewContent);
});

submitPreviewFeedback(payload)

Submit qualitative and quantitative feedback for a preview flag.

src/preview-feedback.js
const feedback = await client.submitPreviewFeedback({
  flagKey: "landing-preview",
  mood: "HAPPY",
  rating: 4,
  description: "The preview looks good and reads clearly.",
  metadata: {
    page: "landing",
    source: "marketing-site",
  },
});

console.log(feedback.success);
console.log(feedback.feedbackId);

Validation:

  • flagKey β€” required
  • mood β€” required (see exported type PreviewFeedbackMood)
  • rating β€” integer from 1 to 5
  • description β€” optional, max 2000 characters
  • metadata β€” optional; when provided, must be a plain object

Code references

submitCodeReferences(payload)

Upload or replace code references for the given branch and flag slugs (same contract as POST /flags/code-refs). Requires apiKey in the SDK config use from CI, static analysis, or server-side code; do not ship the API key to browsers.

src/code-refs.js
const authenticatedClient = new FlagsSDK({
  baseURL: "https://flags-api.basestack.co/v1",
  projectKey: "your-project-key",
  environmentKey: "your-environment-key",
  apiKey: "your-project-api-token",
});

const result = await authenticatedClient.submitCodeReferences({
  branch: "main",
  projectName: "acme-web",
  repositoryUrl: "https://github.com/acme/web",
  references: {
    "new-checkout": [
      {
        filePath: "src/features/checkout.tsx",
        lineNumber: 42,
        lineContent: "if (flags.newCheckout) {",
      },
    ],
  },
});

console.log(result.success);
console.log(result.message);

Validation:

  • branch β€” required, max 255 characters
  • projectName β€” required, max 150 characters
  • repositoryUrl β€” optional, max 255 characters when provided
  • references β€” object keyed by flag slug (each slug max 150 characters)
  • Each row: filePath (max 2000), lineNumber (positive integer), lineContent (max 4000)

Advanced Usage

Custom Fetch Implementation

For environments without native Fetch API (Node.js < 18), provide a custom fetch implementation:

src/custom-fetch.js
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:

src/error-handling.js
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:

src/flags.ts
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 rendering

2. 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-api.basestack.co/v1"
    : "https://flags-dev.basestack.co/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:

  1. Check Configuration: Verify your projectKey and environmentKey are correct
  2. Check Network: Ensure your application can reach the Basestack API endpoint
  3. Check Console: Look for error messages in the browser console or server logs
  4. Verify Environment Variables: Ensure environment variables are set correctly

Cache Issues

If flags aren't updating:

  1. Clear Cache: Use client.clearCache() to force fresh fetches
  2. Check TTL: Verify your cache TTL isn't too long
  3. Manual Refresh: Fetch flags manually to bypass cache

TypeScript Errors

If you're getting TypeScript errors:

  1. Install Types: Types are included with the package, no additional installation needed
  2. Check Imports: Use import type for type-only imports
  3. Type Assertions: Use type assertions for payloads when needed

Fetch API Not Available

If Fetch API isn't available (Node.js < 18):

  1. Install node-fetch: npm install node-fetch
  2. Provide Custom Fetch: Pass fetchImpl option to SDK config
  3. 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:

src/types.ts
import { FlagsSDK, type Flag, type SDKConfig } from "@basestack/flags-js";

const config: SDKConfig = {
  projectKey: "your-key",
  environmentKey: "your-env-key",
};

const client = new FlagsSDK(config);
const flag: Flag = await client.getFlag("header");

Exported types

The package exports these public types (use import type { … } where you only need types):

  • CacheConfig, SDKConfig
  • Flag, PreviewFlag, PreviewFlagsResponse
  • PreviewFeedbackMood, SubmitPreviewFeedbackPayload, SubmitPreviewFeedbackResponse
  • CodeReferenceRow, SubmitCodeReferencesPayload, SubmitCodeReferencesResponse

Type Definitions

The SDK ships TypeScript definitions. Core config and flag shapes include:

type-definitions.ts
/** 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 Flags API (optional; defaults to `https://flags-api.basestack.co/v1`) */
  baseURL?: string;
  /** Project identification key (required) */
  projectKey: string;
  /** Environment identification key (required) */
  environmentKey: string;
  /** Project API token for authenticated write endpoints such as `submitCodeReferences()` (optional; server-side only) */
  apiKey?: string;
  /** Caching configuration options */
  cache?: CacheConfig;
  /** Custom fetch implementation (optional) */
  fetchImpl?: typeof fetch;
  /** If set, `init()` preloads only these slugs; if omitted, `init()` preloads all flags */
  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

PropertyTypeDescriptionRequiredDefault value
baseURLstringFeature Flags API base URL.falsehttps://flags-api.basestack.co/v1
projectKeystringProject identifier.trueβ€”
environmentKeystringEnvironment identifier.trueβ€”
apiKeystringProject API token for write methods such as submitCodeReferences(). Prefer server-side usage.falseβ€”
preloadFlagsstring[]If set, init() loads only these slugs; if omitted, init() loads all flags.falseβ€”
cache.enabledbooleanEnables or disables caching of flags.falsetrue
cache.ttlnumberTime-to-live for cached flags in milliseconds.false5 * 60 * 1000
cache.maxSizenumberMaximum number of flags to store in the cache.false100
fetchImplfunctionCustom fetch implementation (for Node.js versions before 18).falseglobal.fetch

Available methods

MethodDescriptionReturns
init()Preloads flags per preloadFlags rules (or all flags if preloadFlags omitted).Promise<void>
getFlag(slug)Fetches a single flag by slug.Promise<Flag>
getAllFlags()Fetches all flags for the project and environment.Promise<{ flags: Flag[] }>
getPreviewFlags()Fetches preview flags with rendered HTML content.Promise<PreviewFlagsResponse>
submitPreviewFeedback(payload)Submits feedback for a preview flag.Promise<SubmitPreviewFeedbackResponse>
submitCodeReferences(payload)Uploads code references (requires apiKey).Promise<SubmitCodeReferencesResponse>
clearCache()Clears the entire in-memory cache.void
clearFlagCache(slug)Clears cached entries for one slug.void

Method details

init()

Preloads flags into the internal cache. If preloadFlags is set, only those slugs load; if preloadFlags is omitted, all flags load.

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 data

getAllFlags()

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");

getPreviewFlags()

Returns preview flags with previewContent and related fields. See Preview flags and feedback.

submitPreviewFeedback(payload)

Submits preview feedback. See Preview flags and feedback for fields and validation.

submitCodeReferences(payload)

Uploads code references for the configured branch; requires apiKey in the SDK config. See Code references for the payload and validation rules.

On this page