Dashboard
Node.js

Node.js SDK

Install @toggld/node, create a client, read typed flag values, and handle errors.

@toggld/node evaluates Toggld flags from Node.js servers. It has two APIs from one implementation:

  • @toggld/node: a Promise API. No Effect types appear in its signatures.
  • @toggld/node/effect: an Effect API, with the client as a service and a Layer. See Effect API.

It has two evaluation modes:

  • Remote (default): each evaluation is a request to the Toggld API.
  • Local: the SDK downloads the flag definitions and evaluates in your process. Recommended for long-lived servers. See Local evaluation.

Requirements

  • Node.js 22 or later. Local evaluation uses the global WebSocket that Node.js 22 provides.
  • effect version 4 (^4.0.5) as a peer dependency. Both APIs need it.
  • A secret key that starts with ffk_. Use a server SDK key for evaluation. See API keys.

The package is ESM only.

Install

pnpm add @toggld/node effect

The SDK runs on your application's copy of Effect, so an Effect application shares one runtime with the SDK.

Configure credentials

Store the key and the API URL in your server environment:

.env
TOGGLD_SDK_KEY=ffk_YOUR_SECRET_KEY
TOGGLD_API_URL=https://api.toggld.io

The SDK does not read these variables by itself. Pass them to ToggldClient.create. For a self-hosted Toggld API, set TOGGLD_API_URL to your API origin.

Keep secret keys on trusted servers. Never bundle them into browser code. For client-side code, use a public token.

Create a client

Create one client when your process starts, and reuse it for every evaluation.

flags.ts
import { ToggldClient } from "@toggld/node";

export const flags = await ToggldClient.create({
  sdkKey: process.env.TOGGLD_SDK_KEY!,
  endpoint: process.env.TOGGLD_API_URL!,
  mode: "local", // omit for remote evaluation
});

create resolves when the client is ready. In local mode, it waits for the first flag snapshot, or for up to five seconds. See Readiness.

For every option, see Configuration.

Read flag values

Each flag type has a typed getter. Pass the flag key, a fallback value, and an evaluation context.

const enabled = await flags.getBooleanValue("new-checkout", false, {
  targetingKey: "user-123",
  plan: "pro",
});

if (enabled) {
  // serve the new checkout
}
Flag typeValue methodDetails method
booleangetBooleanValuegetBooleanDetails
stringgetStringValuegetStringDetails
numbergetNumberValuegetNumberDetails
objectgetObjectValue<T>getObjectDetails<T>

Each value method returns the flag value, or your fallback value if evaluation fails. It never throws.

const theme = await flags.getStringValue("checkout-theme", "light", { targetingKey: "user-123" });
const limit = await flags.getNumberValue("cart-item-limit", 25, { targetingKey: "user-123" });
const layout = await flags.getObjectValue("checkout-layout", { columns: 1 }, { targetingKey: "user-123" });

Choose fallback values that are safe for users. Your code receives them during outages, before the client is ready, and when a flag is missing.

Inspect evaluation details

The details methods return the full OpenFeature result. Use them during integration and when you log or debug an evaluation.

const details = await flags.getBooleanDetails("new-checkout", false, {
  targetingKey: "user-123",
});
// details:
{
  flagKey: "new-checkout",
  value: true,
  variant: "on",
  reason: "TARGETING_MATCH",
  errorCode: undefined,
  errorMessage: undefined,
  flagMetadata: { team: "growth" },
}
FieldDescription
flagKeyThe key that you requested.
valueThe flag value, or your fallback value on error.
variantThe served variant name. undefined on error.
reasonSTATIC, TARGETING_MATCH, SPLIT, DISABLED, or ERROR. See reasons.
errorCodeAn OpenFeature error code when reason is ERROR.
errorMessageA human-readable explanation of the error.
flagMetadataThe flag's metadata.

evaluate(key, fallback, context) returns the same details without a type check. Use it when you do not know the flag type in advance.

Evaluate every flag

evaluateAll(context) returns the details for every flag in the environment.

const all = await flags.evaluateAll({ targetingKey: "user-123" });
for (const flag of all) {
  console.log(flag.flagKey, flag.value, flag.reason);
}
  • In remote mode, it sends one request to the bulk OFREP endpoint.
  • In local mode, it evaluates in your process.
  • An entry for a flag that failed has reason: "ERROR" and the value null, because there is no per-flag fallback.
  • If the whole request fails, or the local client is not ready yet, it returns an empty array.

Shared context

Use defaultContext for attributes that apply to every evaluation, such as a region or a service version. The context of each call is merged on top. When both define the same attribute, the per-call value wins.

const flags = await ToggldClient.create({
  sdkKey: process.env.TOGGLD_SDK_KEY!,
  endpoint: process.env.TOGGLD_API_URL!,
  defaultContext: { region: "eu-west", service: "checkout-api" },
});

Close the client

Call close() when your process shuts down. It stops background synchronization, sends the remaining telemetry, and releases resources. Do not use the client after you close it.

process.on("SIGTERM", async () => {
  await flags.close();
  process.exit(0);
});

Error handling

Evaluation never throws. If anything goes wrong, the call resolves to your fallback value. The details methods report what happened in errorCode:

errorCodeCause
FLAG_NOT_FOUNDNo flag with this key exists in the key's environment.
TYPE_MISMATCHThe flag value does not match the getter, for example getBooleanValue on a string flag.
TARGETING_KEY_MISSINGA rollout rule matched, but the context has no usable bucketing key. See Missing bucketing key.
PROVIDER_NOT_READYLocal mode has not received its first snapshot yet.
PARSE_ERRORThe SDK could not decode the API response.
GENERALA network error, a timeout, a server error, a rejected request, or another failure. A rejected request is, for example, HTTP 401 for a wrong key or HTTP 429 for a rate limit. errorMessage then names the HTTP status.

Check reason === "ERROR" or errorCode !== undefined to detect a failure:

const details = await flags.getBooleanDetails("new-checkout", false, { targetingKey: userId });
if (details.errorCode) {
  logger.warn({ flag: details.flagKey, code: details.errorCode, message: details.errorMessage }, "flag fallback");
}

A wrong or revoked key does not throw either. In remote mode, each call returns the fallback value with GENERAL and an errorMessage that names HTTP 401. In local mode, the client never becomes ready, and each call returns the fallback value with PROVIDER_NOT_READY. Check the details during integration so that a wrong key does not go unnoticed.

Other languages

Toggld ships an SDK for Node.js only. Go, Python, Ruby, and other SDKs are not available. For other languages, see OpenFeature and OFREP.

On this page