@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
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
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
Thecontext 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 whencontext.planis 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:
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: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:
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: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.