@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.waitUntilReady() before the first resolution.
Options
| Option | Type | Default | Description |
|---|---|---|---|
orgId | string | required | Organization ID |
projectId | string | required | Project ID |
env | string | required | Environment name ("production", "staging", etc.) |
apiKey | string | required | SDK key (traffical_sk_...) |
baseUrl | string | https://sdk.traffical.io | SDK API base URL |
refreshIntervalMs | number | 60000 | How often to re-fetch the bundle |
localConfig | ConfigBundle | — | Embed a bundle for instant cold-start |
eventBatchSize | number | 10 | Events per batch before flushing |
eventFlushIntervalMs | number | 30000 | Max time before flushing pending events |
trackDecisions | boolean | true | Emit decision events alongside exposures |
attributionMode | "cumulative" | "decision" | "cumulative" | Track-event attribution strategy |
Resolving parameters
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 returns a full decision record with decisionId and the layer-by-layer assignments — useful when you want to pass the decision ID downstream:
Tracking events
Track options
| Field | Type | Required | Description |
|---|---|---|---|
unitKey | string | yes | The user ID (or whatever the project’s unit key is) |
properties | object | no | Event payload — anything relevant |
decisionId | string | no | Tie this event to a specific decision (cross-process) |
Express middleware pattern
Type-safe events
Generate TypeScript types from your event definitions to catch invalid event names and properties at compile time: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.