Skip to main content
@traffical/node is the server-side SDK for Node.js and Bun. It fetches the config bundle on startup, resolves parameters locally per request, and ships events to Traffical in the background.

Installation

Initialization

Use createTrafficalClient (async) when you can await at startup — it waits for the first bundle fetch. If you can’t await (e.g. in module top-level code where top-level await isn’t available), use createTrafficalClientSync and call await traffical.waitForReady() before the first resolution. waitForReady() resolves once the first config load completes — and still resolves (never hangs) when the SDK fails open on an unavailable or malformed bundle.

Options

Deprecated option aliases. Earlier releases used eventBatchSize, eventFlushIntervalMs, and a single requestTimeoutMs. These still work and forward to the canonical names above (batchSize, flushIntervalMs, and the three-way timeout split). requestTimeoutMs is honored as the legacy fallback for any of configTimeoutMs / eventsTimeoutMs / resolveTimeoutMs that you don’t set explicitly. Prefer the canonical names — the aliases will be removed in a future major.

Resolving parameters

Both getParams(context, defaults) and decide(context, defaults) take context first, defaults second, and both are synchronous.
The legacy object-bag form — getParams({ context, defaults }) / decide({ context, defaults }) — still works but is deprecated. Prefer the positional form above.

Context

The context object is used for:
  • Bucketing — the unit key (typically userId, as configured on the project) drives which allocation the user gets.
  • Targeting — every field is available for policy condition evaluation. A policy with the condition plan in ["pro", "enterprise"] only applies when context.plan is one of those values.

Defaults

Always pass defaults for every parameter you read. They’re the fallback when no policy matches, when the bundle isn’t loaded yet, or when Traffical is unreachable — and they make your code’s intent explicit even when there’s no active experiment.

Decisions vs getParams

getParams returns just the resolved values. decide (also synchronous) returns a full DecisionResult — a decisionId, the resolved assignments, and per-decision metadata (unit key value, per-layer resolution rows, config version) — useful when you want to pass the decision ID downstream:
See decisions & attribution for when this matters.

Tracking events

track(event, properties?, options?) — the event name first, an optional properties payload second, and an options bag third:

Track arguments

TrackEventOptions fields:

Express middleware pattern

Type-safe events

Generate TypeScript types from your event definitions to catch invalid event names and properties at compile time:
Instantiate the client with your generated event map and track is type-checked against it — event names and their property shapes:

Error handling

The SDK never throws during resolution. If the bundle isn’t loaded yet — or if it never loaded successfully — getParams returns your defaults. Initialization errors surface as a rejected promise:
You can keep a null client around and guard your calls — but more often it’s cleaner to use createTrafficalClientSync, which returns a usable client immediately (resolution falls back to defaults until the bundle arrives) and never rejects at construction.

Shutdown

Flush pending events before your process exits:
This sends any buffered events and stops the background refresh.

Next steps

A/B testing

Run a static experiment.

Canonical experiments

Patterns for backend, batch, and cross-surface tests.

CLI

Manage parameters as code.

API reference

The endpoints the SDK calls.