Node.js SDK

Install and integrate the ABMeter Node.js server SDK

Installation

Install the @abmeter/node package from npm:

npm install @abmeter/node

Node.js 20 or newer is required. ESM and CommonJS are both supported, with TypeScript types included.

This is the server-side SDK: it holds a secret API key and resolves assignments in your Node.js process. For code that runs in web pages, use the JavaScript (Browser) SDK instead.

Configuration

Configure the SDK once at application startup, before handling requests:

import * as abmeter from '@abmeter/node';

abmeter.configure({ apiKey: process.env.ABMETER_API_KEY });

The apiKey is required — use a secret key, never a publishable (pk_) one. You can find or create API keys on the API Keys page (available after signing in).

Core Concepts

Parameters represent feature variations - a parameter has a set of possible values assigned to users through experiments or feature flags. Use resolveParameter to get the value for a specific user.

Events are user actions you want to measure - page views, purchases, clicks. Track them with trackEvent to analyze how parameter variations affect user behavior.

Users are identified by a userId - the randomization unit the SDK hashes to assign parameter values randomly across your users but consistently for each one, so the same userId always sees the same variation (no stored state, no per-call network round-trip). A "user" is your end-user - a customer, visitor, or account - and userId can be any string you choose (a customer id, visitor id, account id). An optional email may be provided; ABMeter uses it only for audience rules that target email patterns (e.g. @acme.com), never for identity or bucketing.

Resolve a Parameter

Create a user and resolve a parameter to get the assigned value:

const user = new abmeter.User({ userId: 'user-123' });

const value = await abmeter.resolveParameter(user, 'checkout-button-color');
// => 'blue' (the assigned variant's value for this parameter)

The return value is the parameter value from the variant assigned to this user. If no experiment or feature flag controls the parameter, the promise resolves to the parameter's default value. Resolution happens locally against a cached assignment config - the promise only awaits the network on the very first call after startup.

email is optional and safe to omit - do so for anonymous or server-side users, such as visitors keyed by a cookie id. Leaving it out raises no error: a user with no email simply never matches an audience that targets email patterns, so email-predicate feature flags and experiments don't apply to them. Every other path (random experiments, user-list audiences, event tracking) uses userId alone. Pass an email only when you use email-pattern audiences: new abmeter.User({ userId: 'user-123', email: '[email protected]' }).

Track an Event

Record user actions to measure the impact of your experiments:

abmeter.trackEvent('purchase', user.userId, { price: 49.99, currency: 'USD' });

Events are queued and submitted asynchronously in batches, so tracking calls are fast and non-blocking. The third argument is a data object with any properties relevant to your metrics. On shutdown, call await abmeter.reset() to flush anything still queued.

Full Example

A realistic Express checkout flow showing parameter resolution and event tracking together:

import express from 'express';
import * as abmeter from '@abmeter/node';

abmeter.configure({ apiKey: process.env.ABMETER_API_KEY });

const app = express();
app.use(express.json());

app.get('/checkout', async (request, response) => {
  const user = new abmeter.User({
    userId: String(request.user.id),
    email: request.user.email, // optional, only used for email-predicate audiences
  });
  const buttonColor = await abmeter.resolveParameter(user, 'checkout-button-color');

  response.render('checkout', { buttonColor });
});

app.post('/checkout', async (request, response) => {
  const order = await Order.create({ userId: request.user.id, ...request.body });

  abmeter.trackEvent('purchase', String(request.user.id), {
    price: order.total,
    currency: order.currency,
  });

  response.redirect(`/orders/${order.id}`);
});

process.on('SIGTERM', async () => {
  await abmeter.reset(); // flush queued exposures/events before exit
  process.exit(0);
});

Next Steps