Node.js SDK reference
Every option, method and return value in @spectry/spectry.
A complete reference for @spectry/spectry. For a walkthrough, start with the Node.js SDK guide.
Client options
new SpectryClient({
siteId: "...", // required
secretKey: "...", // required — sk_live_… / sk_test_…
apiHost: "https://api.spectry.io",
refreshInterval: 60000, // ms between config polls; 0 disables polling
timeout: 3000, // ms per network call
eventFlushInterval: 5000, // ms a queued event may wait before sending
eventBatchSize: 25, // events per request
maxQueuedEvents: 1000, // queue ceiling; oldest are dropped past it
logger: console, // or false to silence the SDK
fetch: customFetch, // for a proxy or a custom HTTP agent
onError: (err, { operation }) => reportToSentry(err),
});
The event queue is bounded deliberately. An unbounded one would turn an outage on our side into your process running out of memory; past the ceiling the oldest events are dropped and the loss is logged.
SpectryClient
init(options?)— load the configuration and start refreshing. Resolvestrue/false; takes{ timeout, throwOnFailure }. Calling it twice does not fetch twice.createScopedInstance(context?)— aScopedSpectryfor one user. Synchronous, no network call.refresh()— refresh the configuration now. Resolvestrue/false, never throws.flush()— send queued events immediately.close()— stop polling and flush. Safe to call twice.ready— whether a configuration has loaded.getConfig()— the cached configuration, ornull. Treat as read-only.getStatus()— cache and queue state, for health checks.
User context
client.createScopedInstance({
attributes: { id: "user_123", country: "DE", plan: "scale" },
visitorId: "anon_abc", // optional — overrides attributes.id for bucketing
sessionId: "sess_xyz", // optional — ties events to a browser session
});
visitorId is useful when you want to bucket on a stable anonymous ID while still reporting a logged-in id. Pass the browser's session ID as sessionId to join server events to the same session the JavaScript SDK is recording.
ScopedSpectry
isOn(key)/isOff(key)— boolean check. An unknown flag is off.getFeatureValue(key, fallback)— the value, or the fallback.getFeature(key)—{ value, on, off, source, variantKey? }.getAllFeatures()— every flag resolved for this user, keyed by flag key.getVariation(id)— the assignment, ornullif not in the experiment.getExperiments()— every experiment this user is currently in.logEvent(name, properties?)— queue an event. Never throws.flush()— send queued events now.ready— whether the configuration has loaded.
Feature sources
What getFeature().source means, and what to do about it:
| Source | Meaning |
|---|---|
enabled | A boolean flag is on for this user. |
variant | A multivariate flag picked a variant; variantKey names it. |
disabled | The flag is switched off in the dashboard. |
targeting | The user did not match the flag's targeting rules — often a missing attribute. |
rollout | The user fell outside the rollout percentage, or has no id to bucket on. |
unknownFlag | No such flag. Check the key, and that it exists on this site. |
notReady | The configuration has not loaded. Check the key and getStatus(). |
Errors
The SDK does not throw from request-path methods. Failures are logged, passed to onError if you supply it, and otherwise absorbed. init() throws only when you pass throwOnFailure: true; the constructor throws immediately if siteId or secretKey is missing.
A SpectryHttpError carries status and isPermanent. A permanent failure (401, 403) means the credential is wrong — check the key, that it belongs to this site, that it has not been revoked, and that it carries the scope the call needs.
Testing
Pass a fake fetch to skip the network entirely and pin the configuration your tests run against:
const client = new SpectryClient({
siteId: "test", secretKey: "sk_test_x",
refreshInterval: 0, logger: false,
fetch: async () => ({
ok: true, status: 200,
headers: { get: () => 'W/"1"' },
text: async () => JSON.stringify({
siteId: "test", version: 'W/"1"', generatedAt: "", experiments: [],
flags: [{
key: "my-feature", type: "boolean", enabled: true,
default_value: false, variants: [], rollout_percentage: 100,
targeting_rules: {},
}],
}),
}),
});
await client.init();
client.createScopedInstance({ attributes: { id: "u1" } }).isOn("my-feature"); // true
refreshInterval: 0 disables polling so tests do not leave a timer running.