All docs
Feature flagsSDKsNode.jscode

Node.js

Read flags on a Node server, with streaming updates and start-up validation.

What you'll have

A server client that resolves flags and learns of a change in about a second.

You'll need
  • An API key from API Keys

Read flags on a Node server, and learn about a change in about a second.

npm install @varia-bly/node

One client per process

waitForInitialization() resolves once variA/Bly has answered, so your first request does not race the first fetch.

import { createClient } from '@varia-bly/node';

const client = createClient(process.env.VARIABLY_API_KEY!, {
  baseUri: process.env.VARIABLY_BASE_URL,
  timeout: 30,
});

await client.waitForInitialization();
OptionDefaultWhat it does
baseUriYour API host. Required: there is no default, so evaluations cannot reach an unintended one.
timeout10Seconds before a request gives up.
streamoffHold a socket open for change events. See below.
onChangeCalled after new values are cached.
cacheSnapshot TTL and revalidation.

Reading flags

// Everything, for one context.
const snapshot = await client.allFlags({ key: user.id });
const flags = snapshot.values();

// One flag, typed.
const redesign = await client.boolVariation('checkoutRedesign', { key: user.id }, false);
const layout = await client.stringVariation('checkoutLayout', { key: user.id }, 'control');

The key in the context is the user identifier — not your API key, and not a gate key. Rules and user overrides match on it, so pass something stable per user. { key: '' } is the anonymous case, which is what a server-rendered page usually wants.

Catch missing flags at start-up

const missing = await client.validateFlags(['checkoutRedesign', 'newPricing']);
if (missing.length > 0) throw new Error(`Flags not defined in Variably: ${missing.join(', ')}`);

A flag the project does not define raises FlagNotFoundError rather than returning the default you passed. That is deliberate: a typo or an unseeded environment would otherwise be indistinguishable from a flag that is switched off, and the application would run on defaults looking healthy. validateFlags surfaces all of them at once, before a request path reaches whichever is read first.

Streaming, and when to leave it off

stream is off unless you pass stream: true, and the client polls either way. Turning it on holds a WebSocket open so the server sees a change in about a second rather than at the next poll.

Leave it off wherever the process is expected to exit. If your build generates pages, an open socket keeps the process alive and the build hangs after the pages are written:

const isBuild = process.env.CLOUD_BUILD === 'true';

const client = createClient(apiKey, { baseUri, stream: !isBuild, timeout: 30 });

A socket that cannot connect, or drops, costs only that latency — polling carries on regardless.

Feeding the browser

If you also render in a browser, resolve on the server and pass the set down, so the first paint is right. See React.

export async function getServerSideProps() {
  const snapshot = await client.allFlags({ key: '' });

  return { props: { bootstrappedFlags: snapshot.values() } };
}