Skip to main content

Experimenting in NodeJS ExpressJS

ExpressJS is a fast, minimalist framework for NodeJS.

ExpressJS stands out as a swift and minimalist framework within the NodeJS ecosystem, offering an optimal solution for developers aiming to implement experiments in their web applications. This guide dives into how you can seamlessly integrate the NodeJS Server SDK into an ExpressJS application, enabling you to dynamically assign users to experiments and manage test groups effectively.

tip

ExpressJS + Statsig Sample App source code here.

The readme within details the steps on installing the app and its dependencies, setting the necessary environment variables, and running the app. Those concepts won't be covered in this guide because they are standard NodeJS practices.

Creating Your First Experiment with Statsig in NodeJS ExpressJS

The below command can be run to automatically create the simple express_app_test experiment in Statsig that gets used throughout this integration guide and demo app. The experiment randomizes based on userID, has a 50/50 test group split, and implements a single parameter. Make sure to replace the authorization key in the header with your key generated in Statsig project settings as documented here.

curl --request POST 'https://statsigapi.net/console/v1/experiments' \
--header 'STATSIG-API-KEY: console-REPLACE_WITH_YOURS' \
--header 'Content-Type: application/json' \
--data-raw '{"name":"express_app_test","idType":"userID","status":"active","hypothesis":"Running Statsig in express works nicely!","primaryMetrics":[{"name":"dau","type":"user"}],"primaryMetricTags":[],"secondaryMetrics":[{"name":"dau","type":"user"}],"secondaryMetricTags":[],"groups":[{"name":"Control","size":50,"parameterValues":{"variant":"control"}},{"name":"Test","size":50,"parameterValues":{"variant":"test"}}],"allocation":100,"duration":14}'

# Note that the above request creates an Unstarted experiment
# Either start the experiment via Console or via this command:
curl --request PUT 'https://statsigapi.net/console/v1/experiments/express_app_test/start' \
--header 'STATSIG-API-KEY: console-REPLACE_WITH_YOURS'

image

Project Setup for Experimentation in NodeJS ExpressJS

This example was built on the very simple Hello World example app documented by ExpressJS. The main server entrypoint is app.js and the demo webpage source follows the handlebars standard, living in views/index.hbs.

image

Aside from statsig-node, this example app requires several node modules, including:

  • cookie-parser for managing cookies for user identity
  • uuidv4 for generating a random uuid
  • express as the NodeJS web framework
  • express-handlebars as the template engine
  • dotenv for secret management

Initializing the Statsig SDK

Every expressJS app has the call to boot the server and listen for requests. This is a convenient place to initialize the Statsig SDK. Here is an example of how that can be achieved:

app.listen(port, async () => {
const runtimeEnv = process.env.STATSIG_ENV_TIER || 'production';
await statsig.initialize(process.env.STATSIG_SERVER_KEY, {
environment: { tier: runtimeEnv },
});
console.log(`Example app listening in ${runtimeEnv} on port ${port}`);
});

Managing Identity

Any Statsig SDK method call requires a userObject to be passed in. For known users that are logged in and an identity is accessible, this is trivial. In the case where identity isn't available, it's ideal to either (a) read an existing cookie ID available to the app or (b) create and use a new uid for the purposes of anonymous assignment within the Statsig SDK. ExpressJS offers a convenient middleware pattern that allows this business logic to be defined once, and it can be used globally in any route handler. Here is an example of what that looks like:

Generating and setting an anonymous uid requires installing uuidv4 and cookie-parser module

// earlier in the source, prior to app.listen call
app.use(cookieParser());

/**
* global middleware to set a cookie if it doesn't exist
* passes the uuid down to route handlers on req object
*/
app.use(async (req, res, next) => {
let statsigDeviceID;
if (req.cookies['statsig_uuid']) {
statsigDeviceID = req.cookies['statsig_uuid'];
}
else {
statsigDeviceID = uuidv4();
res.cookie('statsig_uuid', statsigDeviceID);
}
req.statsigDeviceID = statsigDeviceID;
next();
});

Assign user to experiment

In your route handler, you will have access to the global statsig singleton. Whenever you need to assign a user to an experiment and access the Test Group parameters, you can simply call statsig.getExperiment and pass in the user object. This will return an object that contains the experiment config, where you can access the assigned parameters using get("<param key>", <default value>). You can read more on this approach and the best practices here.

app.get('/', async (req, res) => {
const user = {
// use the random uuid in cookie as the userID
// in practice, you'd use a known userID here
userID: req.statsigDeviceID,
country: 'US',
custom: {
employee: true
}
};
const testGroup = statsig.getExperimentSync(user, 'express_app_test').get('variant', 'control');
res.render('index.hbs', {
testGroup,
});
});

Using assignments in the HTML page

The template will be rendered and with access to the testGroup (string) variable, which gets used to display You are in "Group". The rendered template will be served to the user as an HTML document.

<body class="has-bg">
<div id="main">
<h1>Statsig sample Express app</h1>
<h3>You are in "{{testGroup}}"</h3>
</div>

image