JavaScript / Node.js SDK

The ReleaseAnchor JavaScript SDK works in both Node.js and browser environments. It is the fastest way to evaluate feature flags from JavaScript and, when needed, report Smart Insights feedback back to ReleaseAnchor.

📦 npm: npmjs.com/package/@release-anchor/js

Installation

npm install @release-anchor/js
# or
pnpm add @release-anchor/js
# or
yarn add @release-anchor/js

First integration

Before you write SDK code, set up the product side in the dashboard:

  • Sign in to Release Anchor
  • Create a project and its environments
  • Create an environment API key
  • Create the flag you want to evaluate

If you plan to use Smart Insights, enable it for the flag in the dashboard before integrating the feedback helpers below. See Smart Insights →.

Quick start

import { ReleaseAnchor } from "@release-anchor/js";

const client = new ReleaseAnchor({
  apiKey: process.env.RELEASE_ANCHOR_KEY,
});

const result = await client.evaluate("dark-mode", "user-123");

if (result.value) {
  // feature is on for this user
}

evaluate() returns an EvaluateResponse object with:

  • value — the boolean result you should use to gate your feature
  • matchedRuleType"STATIC" | "SEGMENT" | "PERCENTAGE" | null in canonical uppercase
  • error — populated on technical failures, null on successful API responses
  • evaluationId — present when Smart Insights feedback is enabled for the evaluated flag

Primary API surface

Most integrations only need these two methods:

  • evaluate(flagKey, userIdentifier, defaultValue?)
  • evaluateBulk(flagKey, userIdentifiers[], defaultValue?)

Configuration

const client = new ReleaseAnchor({
  apiKey: "<YOUR_API_KEY>",  // Required - get from the API Keys page
  apiVersion: "v1",          // "v1" | "v2"
  baseUrl: "https://...",    // Override API base URL
  timeout: 5000,             // Request timeout in ms
  defaultValue: false,       // Fallback value on technical errors
  strict4xx: false,          // Throw StrictHttpError on unexpected 4xx
  logger: console.warn,      // Called on technical errors
});
OptionTypeDescription
apiKeystringRequired. Your environment API key.
apiVersion"v1" | "v2"API version used by the client.
baseUrlstringOverride the API base URL.
timeoutnumberRequest timeout in milliseconds.
defaultValuebooleanReturned on technical failures such as network or timeout errors.
strict4xxbooleanThrows StrictHttpError on unexpected 4xx responses instead of silently falling back.
logger(message: string, context?: unknown) => voidCalled on technical errors for observability and debugging.

evaluate(flagKey, userIdentifier, defaultValue?)

Evaluates a single flag for a user. Concurrent calls for the same flagKey + userIdentifier + defaultValue are deduplicated while the request is still in flight.

const result = await client.evaluate("dark-mode", "user-123");

// Per-call defaultValue overrides the instance-level default
const fallbackResult = await client.evaluate("dark-mode", "user-123", true);
ParameterTypeDescription
flagKeystringThe flag key to evaluate. Case-sensitive.
userIdentifierstringA stable user identifier used for targeting and rollout consistency.
defaultValuebooleanOptional per-call fallback override.

evaluateBulk(flagKey, userIdentifiers[], defaultValue?)

Evaluates one flag for multiple users in a single request.

const results = await client.evaluateBulk("dark-mode", ["user-1", "user-2"]);

for (const [userId, result] of Object.entries(results)) {
  if (result.value) {
    console.log(`${userId}: feature on`);
  }
}

Missing keys in the server response are filled with a fallback entry. Extra keys are ignored.

Smart Insights

Use the Smart Insights helpers when you want the SDK to evaluate a flag and also report execution outcomes.

Smart Insights connects evaluation events with real execution results such as success, failure, and latency. Use it when you want feedback data in the dashboard instead of evaluation-only visibility.

executeWithFeedback(flagKey, userId, handler)

Single-user helper that:

  • calls evaluate(...) first
  • runs your handler with the evaluation result
  • calls reportSuccess(...) or reportFailure(...) automatically when evaluationId is present
const result = await client.executeWithFeedback(
  "checkout-redesign",
  "user-123",
  async (evaluation) => {
    if (!evaluation.value) return false;
    return runCheckoutExperience();
  }
);

Handler semantics:

  • return true → reports success
  • return false → reports failure with EXECUTION_FAILED
  • throw → reports failure with UNKNOWN and rethrows the original error

executeWithFeedback(flagKey, userIds[], handler)

Bulk helper that:

  • calls evaluateBulk(...) first
  • runs the handler independently for each user
  • sends one bulk feedback request after processing users with evaluationId
const results = await client.executeWithFeedback(
  "checkout-redesign",
  ["user-1", "user-2", "user-3"],
  async (userId, evaluation) => {
    if (!evaluation.value) return false;
    return runExperienceForUser(userId);
  }
);

In bulk mode, handler errors do not stop the rest of the users from being processed. The returned value is Record<string, boolean>.

Manual reporting

Use these methods when you want to report execution results manually instead of relying on executeWithFeedback(...).

reportSuccess(evaluation, options?)

Reports a successful execution outcome for a previous evaluation. If evaluation.evaluationId is missing, the call becomes a no-op.

const evaluation = await client.evaluate("checkout-redesign", "user-123");

await client.reportSuccess(evaluation, {
  latencyMs: 125,
});

reportFailure(evaluation, options?)

Reports a failed execution outcome for a previous evaluation. If evaluation.evaluationId is missing, the call becomes a no-op.

const evaluation = await client.evaluate("checkout-redesign", "user-123");

await client.reportFailure(evaluation, {
  errorType: "EXECUTION_FAILED",
  latencyMs: 125,
});

Cleanup

Call destroy() during test teardown or application shutdown if you want to clear the in-flight request deduplication state explicitly:

client.destroy();
// afterEach(() => client.destroy()); // in test suites

Error handling

Technical errors like network failures, timeouts, 401, 429, 5xx, or response parse failures are caught internally, logged via logger, and returned as fallback responses.

const result = await client.evaluate("dark-mode", "user-123");

if (result.error) {
  // result.error.type: "NETWORK_ERROR" | "TIMEOUT" | "UNAUTHORIZED" |
  //                    "RATE_LIMITED" | "HTTP_ERROR" | "PARSE_ERROR"
  // result.error.message: string
}

reportSuccess(), reportFailure(), and the feedback side of executeWithFeedback() are best-effort. They swallow network and HTTP failures so feedback delivery never breaks your application flow.

strict4xx

Set strict4xx: true to throw StrictHttpError on unexpected 4xx responses instead of silently falling back. This is useful for catching integration issues during development.

import { ReleaseAnchor, StrictHttpError } from "@release-anchor/js";

const client = new ReleaseAnchor({ apiKey: "...", strict4xx: true });

try {
  await client.evaluate("dark-mode", "user-123");
} catch (err) {
  if (err instanceof StrictHttpError) {
    console.error("Unexpected HTTP error:", err.status);
  }
}

Timeouts are still returned as fallback responses, not thrown.

Node.js example

import { ReleaseAnchor } from "@release-anchor/js";

const client = new ReleaseAnchor({
  apiKey: process.env.RELEASE_ANCHOR_KEY,
});
const checkoutRoute = process.env.CHECKOUT_ROUTE;

app.get(checkoutRoute, async (req, res) => {
  const result = await client.evaluate("new-checkout", req.user.id);

  if (result.value) {
    return res.render("checkout-v2");
  }

  return res.render("checkout-v1");
});

Browser example

import { useEffect, useState } from "react";
import { ReleaseAnchor } from "@release-anchor/js";

const client = new ReleaseAnchor({
  apiKey: process.env.NEXT_PUBLIC_RELEASE_ANCHOR_KEY,
});

export function CheckoutButton({ userId }) {
  const [useNewFlow, setUseNewFlow] = useState(false);

  useEffect(() => {
    client.evaluate("new-checkout", userId).then((r) => setUseNewFlow(r.value));
  }, [userId]);

  return useNewFlow ? <NewCheckout /> : <LegacyCheckout />;
}
Browser API key exposure

When using the SDK in the browser, your API key is visible in client-side code. Use a read-only evaluation key and make sure your flag responses do not expose sensitive business logic.

TypeScript

The SDK ships with full TypeScript types. No @types package is required.

import {
  ReleaseAnchor,
  type EvaluateResponse,
  StrictHttpError,
} from "@release-anchor/js";

const client = new ReleaseAnchor({ apiKey: process.env.RELEASE_ANCHOR_KEY! });
const result: EvaluateResponse = await client.evaluate("my-flag", userId);
Was this helpful?