Step-by-step installation and configuration of the AB Tasty Flagship Node.js SDK for server-side applications.
Before starting, verify:
- Node.js 6.0.0+ installed
- NPM 3.0.0+ (or yarn/pnpm)
- Server/device has internet access
- AB Tasty credentials (Environment ID and API Key)
Finding credentials: AB Tasty account → Settings → Feature Experimentation → Environment settings
# npm
npm install @flagship.io/js-sdk
# yarn
yarn add @flagship.io/js-sdk
# pnpm
pnpm add @flagship.io/js-sdkVerify: Package appears in package.json dependencies.
Initialize SDK early in your application entry point:
import { Flagship } from "@flagship.io/js-sdk";
// Start SDK with credentials
await Flagship.start("<ENV_ID>", "<API_KEY>");When to initialize: Before server starts handling requests, typically in main entry point.
Optional configuration:
import { Flagship, LogLevel, DecisionMode } from "@flagship.io/js-sdk";
await Flagship.start("<ENV_ID>", "<API_KEY>", {
logLevel: LogLevel.INFO,
timeout: 2, // seconds (default: 2, minimum: 0)
fetchNow: true, // auto-fetch flags on visitor creation (default: true)
disableCache: false, // enable visitor and hit cache (default: false)
decisionMode: DecisionMode.DECISION_API, // default mode
});Create visitors with unique IDs and context for targeting:
const fsVisitor = Flagship.newVisitor({
visitorId: "<VISITOR_ID>", // e.g., req.user?.id || "anonymous"
hasConsented: true, // Required for GDPR
context: {
isVIP: true,
country: "NL",
plan: "premium",
// Add targeting attributes
},
});Express.js middleware pattern:
app.use((req, res, next) => {
req.fsVisitor = Flagship.newVisitor({
visitorId: req.user?.id || req.sessionID || "anonymous",
hasConsented: true,
context: {
email: req.user?.email,
ipAddress: req.get("x-forwarded-for") || req.ip,
userAgent: req.get("user-agent"),
},
});
next();
});Before using flags, fetch them from AB Tasty:
await fsVisitor.fetchFlags();Alternative approaches:
// Using promise
fsVisitor.fetchFlags().then(() => {
// Flags ready
});
// Using event listener
fsVisitor.on("ready", () => {
// Flags ready
});Get flag values with defaults:
// Get flag object
const flag = fsVisitor.getFlag("displayVipFeature");
// Get value with default fallback
const showVip = flag.getValue(false);
if (showVip) {
// Enable VIP feature
}Different flag types:
const welcomeText = fsVisitor.getFlag("welcome-text").getValue("Hello");
const apiLimit = fsVisitor.getFlag("api-limit").getValue(100);
const config = fsVisitor.getFlag("ui-config").getValue({ theme: "light" });Important: getValue() automatically sends exposure hit by default.
Send hits to track user actions:
import { HitType, EventCategory } from "@flagship.io/js-sdk";
// Event hit
fsVisitor.sendHit({
type: HitType.EVENT,
category: EventCategory.USER_ENGAGEMENT,
action: "click",
label: "vip-feature-button",
value: 100,
});
// Page view
fsVisitor.sendHit({
type: HitType.PAGE,
documentLocation: "/checkout",
});
// Transaction
fsVisitor.sendHit({
type: HitType.TRANSACTION,
transactionId: "T12345",
affiliation: "Online Store",
totalRevenue: 99.99,
shippingCosts: 5.0,
taxes: 8.5,
currency: "USD",
});import { Flagship, DecisionMode } from "@flagship.io/js-sdk";
// DECISION_API (default): campaign assignment done server-side, one HTTP request per fetchFlags call
await Flagship.start("<ENV_ID>", "<API_KEY>", {
decisionMode: DecisionMode.DECISION_API,
});
// BUCKETING: downloads all campaign configs once, assignments computed client-side
await Flagship.start("<ENV_ID>", "<API_KEY>", {
decisionMode: DecisionMode.BUCKETING,
pollingInterval: 5, // seconds between bucketing file re-downloads (0 = only once at start)
});
// BUCKETING_EDGE: for Edge environments; SDK must be initialized with the bucketing file;
// no automatic batching — Flagship.close() must be called manually
await Flagship.start("<ENV_ID>", "<API_KEY>", {
decisionMode: DecisionMode.BUCKETING_EDGE,
});Always call close() before your process exits to flush all pending hits:
// In Express / any Node.js app
process.on("SIGTERM", async () => {
await Flagship.close(); // batches and sends all collected hits
process.exit(0);
});
// In BUCKETING_EDGE mode this is mandatoryExpress.js integration:
import express from "express";
import { Flagship, HitType, EventCategory } from "@flagship.io/js-sdk";
const app = express();
// Step 1: Start SDK
await Flagship.start(process.env.FLAGSHIP_ENV_ID, process.env.FLAGSHIP_API_KEY);
// Step 2: Create visitor middleware
app.use(async (req, res, next) => {
req.fsVisitor = Flagship.newVisitor({
visitorId: req.user?.id || "anonymous",
hasConsented: true,
context: {
isVip: req.user?.isVip || false,
country: req.user?.country,
},
});
// Step 3: Fetch flags
await req.fsVisitor.fetchFlags();
next();
});
// Step 4 & 5: Use flags
app.get("/", async (req, res) => {
const welcomeFlag = req.fsVisitor.getFlag("welcome-message");
const welcomeText = welcomeFlag.getValue("Welcome!");
res.send(`<h1>${welcomeText}</h1>`);
});
// Step 6: Track hits
app.post("/checkout", async (req, res) => {
await req.fsVisitor.sendHit({
type: HitType.EVENT,
category: EventCategory.ACTION_TRACKING,
action: "checkout-completed",
});
res.json({ success: true });
});
app.listen(3000, () => console.log("Server running"));Update context dynamically:
fsVisitor.updateContext({
hasCompletedPurchase: true,
cartValue: 150.0,
});
await fsVisitor.fetchFlags(); // Refetch with new contextAccess campaign metadata:
const flag = fsVisitor.getFlag("my-feature");
console.log("Value:", flag.getValue(false));
console.log("Exists:", flag.exists());
console.log("Campaign ID:", flag.metadata.campaignId);
console.log("Variation ID:", flag.metadata.variationId);Track authenticated users:
// On login
fsVisitor.authenticate("<AUTHENTICATED_USER_ID>");
await fsVisitor.fetchFlags(); // recommended after authenticate
// On logout
fsVisitor.unauthenticate();
await fsVisitor.fetchFlags(); // recommended after unauthenticate// Clear all context keys
fsVisitor.clearContext();
await fsVisitor.fetchFlags(); // refetch after clearing contextRetrieve the full collection of flags fetched for a visitor:
const flags = fsVisitor.getFlags(); // returns IFSFlagCollection
console.log("Total flags:", flags.size);
// Iterate
flags.forEach((flag, key) => {
console.log(key, flag.getValue(null));
});
// Check if a flag key exists
console.log(flags.has("my-feature"));
// Expose all flags at once
await flags.exposeAll();By default getValue() sends an exposure hit. To separate retrieval from exposure:
const flag = fsVisitor.getFlag("displayVipFeature");
// Get value WITHOUT sending exposure
const value = flag.getValue(false, false);
// Later, when the visitor actually sees the feature:
await flag.visitorExposed();// Event
{ type: HitType.EVENT, category: EventCategory.USER_ENGAGEMENT, action: "click" }
// Page view
{ type: HitType.PAGE, documentLocation: "/page" }
// Screen (SPA/mobile)
{ type: HitType.SCREEN, documentLocation: "HomeScreen" }
// Transaction (affiliation is required)
{ type: HitType.TRANSACTION, transactionId: "T123", affiliation: "Store", totalRevenue: 100 }
// Item (product)
{ type: HitType.ITEM, transactionId: "T123", productName: "Product", itemPrice: 50 }import { HitType, EventCategory } from "@flagship.io/js-sdk";
// sendHits (plural) accepts an array
await fsVisitor.sendHits([
{ type: HitType.PAGE, documentLocation: "/checkout" },
{
type: HitType.EVENT,
category: EventCategory.ACTION_TRACKING,
action: "checkout",
},
]);Flags return default values:
- Ensure
await fetchFlags()called beforegetValue() - Verify visitor context matches campaign targeting
- Check campaign is active in AB Tasty dashboard
API timeout errors:
- Verify Environment ID and API Key correct
- Check internet connectivity
- Increase timeout in SDK config
GDPR consent errors:
hasConsentedfield required when creating visitor- Set based on user consent status
Context not applied:
- Set context in
newVisitor()or useupdateContext() - Call
fetchFlags()again after context changes
Installation complete when:
- ✅ Package
@flagship.io/js-sdkinstalled - ✅ SDK initialized with
Flagship.start() - ✅ Visitor created with
newVisitor() - ✅ Flags fetched with
fetchFlags() - ✅ Server starts without errors
- ✅ Flag values retrieved successfully
- ✅ Hits sent to analytics
- For runtime flag retrieval: See decision-api.md
- For campaign creation: See fear-resource-extractor.md