Skip to main content

Node.js SDK

Server-side SDK for Node.js applications. Uses native http/https modules (no fetch dependency). For browser applications, see the JavaScript SDK.

Package: @toggletown/sdk-node

Installation

npm install @toggletown/sdk-node

Quick Start

import { ToggleTownClient } from '@toggletown/sdk-node';

const client = new ToggleTownClient('tt_live_your_api_key');
await client.initialize();

// Evaluate a flag
const enabled = client.getBooleanFlag('new-feature', false, {
userId: 'user-123',
plan: 'pro',
});

// Clean up on shutdown
process.on('SIGTERM', () => client.close());

Configuration

const client = new ToggleTownClient('tt_live_your_api_key', {
apiUrl: 'https://api.toggletown.com', // API endpoint (default)
pollingInterval: 30000, // Milliseconds between refreshes (default: 30000)
onError: (error) => { // Error callback for background polling
logger.error('Flag fetch error:', error);
},
});
OptionTypeDefaultDescription
apiUrlstringhttps://api.toggletown.comAPI endpoint URL
pollingIntervalnumber30000Polling interval in ms
onError(error: Error) => voidError callback for background fetch failures

Flag Types

Boolean

const enabled = client.getBooleanFlag('feature-enabled', false, context);

String

const variant = client.getStringFlag('experiment-variant', 'control', context);

Number

const maxItems = client.getNumberFlag('max-items', 10, context);

JSON

const config = client.getJSONFlag('feature-config', { enabled: false }, context);

User Context

Pass user attributes for targeting and rollouts:

const context = {
userId: 'user-123',
email: 'user@example.com',
plan: 'enterprise',
country: 'US',
};

const enabled = client.getBooleanFlag('premium-feature', false, context);

Express Integration

import express from 'express';
import { ToggleTownClient } from '@toggletown/sdk-node';

const app = express();
const flags = new ToggleTownClient('tt_live_xxx');
await flags.initialize();

// Middleware to build flag context from request
app.use((req, res, next) => {
req.flagContext = {
userId: req.user?.id,
plan: req.user?.plan || 'free',
country: req.headers['x-country'],
};
next();
});

app.get('/api/checkout', (req, res) => {
const newCheckout = flags.getBooleanFlag('new-checkout', false, req.flagContext);

if (newCheckout) {
return res.json({ flow: 'new' });
}
res.json({ flow: 'legacy' });
});

// Graceful shutdown
process.on('SIGTERM', () => {
flags.close();
process.exit(0);
});

app.listen(3000);

Fastify Integration

import Fastify from 'fastify';
import { ToggleTownClient } from '@toggletown/sdk-node';

const fastify = Fastify();
const flags = new ToggleTownClient('tt_live_xxx');
await flags.initialize();

fastify.decorate('flags', flags);

fastify.addHook('onRequest', (req, reply, done) => {
req.flagContext = { userId: req.user?.id };
done();
});

fastify.get('/api/feature', (req, reply) => {
const enabled = fastify.flags.getBooleanFlag('feature', false, req.flagContext);
reply.send({ enabled });
});

// Cleanup on close
fastify.addHook('onClose', () => flags.close());

Error Handling

Initialization errors are thrown — catch them to handle startup failures:

try {
await client.initialize();
} catch (error) {
console.error('Failed to initialize flags:', error);
// Continue with defaults or exit
}

Background polling errors are handled silently (logged to console) unless you provide an onError callback:

const client = new ToggleTownClient('tt_live_xxx', {
onError: (error) => {
logger.error('ToggleTown polling error:', error);
},
});

Thread Safety

The Node.js SDK is safe to use across async operations. A single client instance can be shared across your entire application — flag data is managed internally with no race conditions.

Debugging

const allFlags = client.getAllFlags();
console.log(allFlags);

console.log('Initialized:', client.isInitialized());

TypeScript

The SDK exports all types:

import { ToggleTownClient, ToggleTownConfig } from '@toggletown/sdk-node';
import type { EvaluationContext, FlagConfig } from '@toggletown/types';