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.
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'
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
.
Aside from statsig-node
, this example app requires several node modules, including:
cookie-parser
for managing cookies for user identityuuidv4
for generating a random uuidexpress
as the NodeJS web frameworkexpress-handlebars
as the template enginedotenv
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>