Dashboard
Node.js

Local evaluation

Download flag definitions in the background and evaluate in-process, with no request per evaluation.

In local evaluation, the SDK downloads the complete flag definitions for its environment and evaluates them inside your process. It uses the same evaluation engine as the Toggld API, so the results are the same. Use local mode on long-lived servers.

import { ToggldClient } from "@toggld/node";

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

Remote or local

Remote (default)Local
How it evaluatesOne HTTP request per evaluationIn your process, from a synchronized snapshot
Latency per evaluationA network round tripMicroseconds
Works during an API outageNo. Calls return fallback values.Yes, with the last snapshot it received
StartupReady immediatelyWaits for the first snapshot
CredentialSecret key (ffk_)Secret key (ffk_)
Data on your serverEvaluated values onlyAll rules, variants, and referenced segments for the environment
TelemetryThe API counts evaluationsThe SDK sends counts in batches
Best forScripts, short-lived jobs, serverless functionsLong-lived servers

Local mode needs a secret key, because the snapshot contains your targeting rules. Public tokens (ffp_) cannot download it.

How synchronization works

  1. On start, the SDK requests a snapshot from GET /sync/v1/flags. Until the first snapshot arrives, it retries with a growing delay, from 250 ms up to 5 seconds.
  2. With the default ws transport, it also opens a WebSocket stream at /sync/v1/stream. The API pushes a new snapshot after each change, where the stream is available.
  3. It keeps polling the snapshot every sync.pollIntervalMs (default 30 seconds), even while the stream is healthy. Polling recovers changes that the stream missed.
  4. It replaces its snapshot only when the snapshot version changes.

If a request fails, the SDK keeps its last snapshot and tries again. Evaluations continue to use the last snapshot.

Transports

sync.transportBehavior
"ws" (default)Live stream plus polling. If the stream is not available, the SDK keeps polling and retries the stream with backoff, up to about 30 seconds between attempts.
"poll"Polling only.

Hosted API uses polling

The hosted API at https://api.toggld.io does not serve the WebSocket stream yet, so SDKs receive updates by polling. The default ws transport falls back to polling automatically. Set sync: { transport: "poll" } to skip the stream connection attempts.

Readiness

ToggldClient.create waits for the first snapshot, for up to sync.readyTimeoutMs (default 5000 ms). Then it resolves even if no snapshot has arrived, and synchronization keeps trying in the background.

Until the first snapshot arrives:

  • Typed getters return your fallback value.
  • The details report reason: "ERROR" and errorCode: "PROVIDER_NOT_READY".
  • evaluateAll returns an empty array.

To wait for the first snapshot without a time limit, call ready():

const flags = await ToggldClient.create({ sdkKey, endpoint, mode: "local" });
await flags.ready(); // resolves when the first snapshot arrives

ready() never resolves if the key is wrong, so add your own timeout if you use it at startup. In remote mode, ready() resolves at once.

How fast changes arrive

A saved change does not reach every SDK at once. Use these numbers to plan.

Local mode on the hosted API. The SDK polls every sync.pollIntervalMs. The hosted API can serve a snapshot from an edge cache for up to 60 seconds after a change. Plan for the poll interval, plus up to 60 seconds of cache age, plus request time. Changes often arrive sooner, but Toggld does not promise a fixed delivery time.

Remote mode on the hosted API. Each evaluation reads the API. The same edge cache can serve flag data for up to 60 seconds after a change.

Local mode on a self-hosted Node.js API. A change pushes to the SDKs that are connected to the same server process that saved it. Other processes learn about the change through polling. With successful requests, the conservative delivery bound is pollIntervalMs + 2 × timeoutMs, plus scheduling delays. With the defaults, that is 40 seconds. All API processes must read the same Postgres database. If you read from a replica, add its replication delay.

During network outages or API failures, there is no fixed delivery bound. The SDK converges after requests succeed again.

Get updates faster

Lower sync.pollIntervalMs to poll more often:

const flags = await ToggldClient.create({
  sdkKey, endpoint, mode: "local",
  sync: { pollIntervalMs: 10_000 },
});

Each poll downloads a full snapshot. Snapshot polls and remote evaluations with secret keys share one rate limit of 1,200 requests per minute per environment. That budget covers every client that uses a secret key in the environment. For example, 100 local clients that poll every 10 seconds use 600 requests per minute. Use a positive, finite interval.

Performance

A loopback benchmark on a laptop compared the two modes with the same workload:

ModeThroughputMedian latency
Remote (OFREP over HTTP)about 11,600 evaluations per second2.4 ms
Local (in-process engine)about 1,090,000 evaluations per second27 µs

These numbers come from a local test, not from the hosted API. Over the internet, remote evaluation also pays the network round trip.

Segments

The snapshot includes the segments that your flags reference, with their rules and member lists. Segment membership resolves in your process.

API runtimes

The Toggld API runs in two runtimes, and synchronization differs between them:

API runtimeStreamUpdates
Cloudflare Workers (the hosted API at api.toggld.io)Not available yetPolling
Node.js (self-hosted)AvailableStream pushes plus polling

The SDK itself targets Node.js 22 or later. It uses Node.js APIs, such as node:fs for flag files. Toggld does not document other runtimes for the SDK.

Telemetry

In network-synchronized local mode, the SDK counts evaluations and sends the counts in batches every 30 seconds and on close(). These counts feed Analytics. Remote mode sends no SDK telemetry, because the API counts those evaluations. See Telemetry.

Evaluate from a file

Local mode can also read its snapshot from a file instead of the network. Use this for local development, tests, and CI. See Flag files.

On this page