A Guide to Getting Started Quickly With NodeJS Feature Flags

Written by: Erik Francis
10 min read

We've all done it at some point: thrown a conditional around a piece of code to enable or disable it. When it comes to feature flags, this is about as simple as it gets. But it's far from really taking advantage of the power of feature flags (also called feature toggles) to control functionality in our application.

This guide will walk you through implementing a feature flag in NodeJS using the CloudBees Feature Management service. At the end of the article, you'll have a dead simple application that integrates with the feature flag service.

Ready? Good. Let’s get going!

Starting With the Basics

First, let's do some initial setup to make following this article easier. While we could create an entire NodeJS Express web application, we'll keep things simple and create a basic Hello CloudBees Feature Management application.

First, let’s create a new directory for our project and generate a package.json file using the npm init command.

$ mkdir rollout-nodejs-demo && cd $_
rollout-nodejs-demo $ npm init

Follow along with the prompts from the npm init command and just accept the defaults. After all of that, you'll have a new, bare-bones package.json file.

{
  "name": "rollout-nodejs-demo",
  "version": "1.0.0",
  "description": "Example of using Rollout feature flags in NodeJS",
  "main": "app.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "..."
  },
  "author": "Casey Dunham",
  "license": "MIT",
  "bugs": {
    "url": "..."
  },
  "homepage": "...",
  "dependencies": {   
  }
}

Now, open up your favorite text editor, and let’s create our app.js file. The app.js file will be our application's main entry point.

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  var message = '<h1>Hello Rollout!</h1>’;

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html');
  res.end(message);
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Let’s run our application and make sure we have it all in working order.

rollout-nodejs-demo $ node app.js
Server running at http://127.0.0.1:3000/

Finally, open your browser and navigate to http://127.0.0.1:3000/, where you'll see our venerable greeting.

Alright, now that we've verified that everything is working, let’s get going and create our first feature flag.

Your First NodeJS Feature Flag

Our application is functioning, but it's not very exciting. Now, let's say one day our product manager decides we need to show our visitors a motivational message. “No problem!” we say, and since we really like this product manager, their request goes to the head of our task queue.

After a long meeting, the business decides what they want for a message. Don't ask; we weren't invited to the meeting. Let’s go back to the app.js file and update it to include the new message. Based on our experience, we know how these requests can sometimes go, so we wrap the message in a conditional. This will allow us to easily turn it off later if they decide they don’t want to show it anymore. Open up your app.js, and let's code this up before calling it a day.

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const showMotivationalQuote = true;

const server = http.createServer((req, res) => {
  var message = '<h1>Hello Rollout!</h1>';
  if (showMotivationalQuote) {
    message += "<h3>The number zero. It's whatever you make of it.</h3>"
  }

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html');
  res.end(message);
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Now run the application to see your new message in all its wonderfully obtuse glory.

Congratulations! You've created your first feature flag in NodeJS. Now feel free to head home since you've had such a productive day.

Introduction to Feature Flag Management

Right now, you're probably wondering about the point of what we just did. I don’t blame you. Our feature flag is a bit lackluster. It doesn’t have any of the benefits of what we can get out of a proper feature flag. Feature flags are intended to help us release new functionality safely and without changing code. In order to achieve those goals, we need to do a bit more.

We also haven't solved any problems with our current code. In fact, we wrote some code we didn't have to. Feature flags can quickly get us into technical debt if we aren't careful. We'll get to how to deal with that later in this article, so stick around.

Getting the Configuration Out of Code

Before moving on, let's move the feature flag configuration outside of our code. NodeJS will parse JSON files and bind them to a variable for us when using the require statement. Create a config.json file and add the following contents to it:

{
  "showMotivationalQuote": true
}

Go back to your app.js file and update it to the following:

const http = require('http');
const config = require('./config.json');

const hostname = '127.0.0.1';
const port = 3000;

const showMotivationalQuote = config.showMotivationalQuote;

const server = http.createServer((req, res) => {
  var message = '<h1>Hello Rollout!</h1>';
  if (showMotivationalQuote) {
    message += "<h3>The number zero. It's whatever you make of it.</h3>"
  }

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html');
  res.end(message);
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

We've now moved the configuration outside of the code. However, if we want to change this, we'll still need to update the configuration file and redeploy our application. We could handle the setting via an environment variable, but that doesn't solve the problem. It just moves it to another layer of indirection. Implementing more flags in this manner will quickly make things complex and error-prone. And you know there will only be more changes coming. What we really want is to control the feature flag, and we want to do it without touching or releasing new code. In other words, what we're really looking for is a way to centrally manage feature flags.

Centrally Managing Feature Flags

At this point, you created a feature flag as a simple conditional and variable. Then, you took it a step further to move the value into a configuration file. This demonstrates the concept but doesn't help us out much. We still need to redeploy our application.

Companies such as Google and Facebook use feature flags to perform dark launches and split testing. In order to reap the kinds of benefits these companies enjoy by using feature flags, we need to do more than just use a hard-coded variable.

To turn feature flags into the powerful tool that they are, we need to centrally manage them. Luckily for us, you can do this as a service in NodeJS using CloudBees Feature Management.

Getting Started With CloudBees Feature Management

CloudBees Feature Management will let you get started for free, so go ahead and create a CloudBees Feature Management account. Then sign in and create a new app. Under "Installation Type," select Javascript as the language and NodeJS as the platform.

After clicking the "Start Installation" button, we'll see a screen that describes how to use the SDK from NodeJS. Let's follow the instructions on the CloudBees Feature Management "Javascript SDK" window.

We'll go through each of these steps here, but refer to the NodeJS documentation for more information.

Let’s install the Rox-node CloudBees Feature Management client package.

rollout-nodejs-demo $ npm i rox-node —save

This will update your NodeJS package.json file to include the Rox-node dependency. After running the command, your package.json file should look like the following:

{
  "name": "rollout-nodejs-demo",
  "version": "1.0.0",
  "description": "Example of using Rollout feature flags in NodeJS",
  "main": "app.js",
  "scripts": {
    "test": "echo "Error: no test specified" &amp;&amp; exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/caseydunham/rollout-nodejs-demo.git"
  },
  "author": "Casey Dunham",
  "license": "ISC",
  "bugs": {
    "url": "..."
  },
  "homepage": "...",
  "dependencies": {
    "rox-node": "^2.0.2"
  }
}

Next, we need to open up the confg.json file and add our API key into it.

{
  "roxAPIKey": "5a68f9d5f807d908a1426405",
  "showMotivationalQuote": true
}

Finally, update your app.js file to read in the API key and add the call to Rox.setup().

const http = require('http');
const Rox = require("rox-node");

const config = require('./config.json');

const hostname = '127.0.0.1';
const port = 3000;

const showMotivationalQuote = config.showMotivationalQuote;

const server = http.createServer((req, res) => {
  var message = '<h1>Hello Rollout!</h1>';
  if (showMotivationalQuote) {
    message += "<h3>The number zero. It's whatever you make of it.</h3>"
  }

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html');
  res.end(message);
});

server.listen(port, hostname, () => {
  Rox.setup(config.roxAPIKey);
  console.log(`Server running at http://${hostname}:${port}/`);
});

Implementing Feature Flags

Once all that is done, go back to the CloudBees Feature Management administration screen. You'll see that it’s waiting for you to re-run your application. Do that, and the CloudBees Feature Management administration screen will reward you with a happy success screen.

At this point, we've successfully integrated the CloudBees Feature Management service into our application. We haven’t created any feature flags yet, though, so let's get to work on that.

We'll create a flags object that will act as a container for all of our feature flags. Go back to your app.js file and add this object. We'll also register this object with the Rox client before the setup call.

const http = require('http');
const Rox = require("rox-node");

const config = require('./config.json');

const hostname = '127.0.0.1';
const port = 3000;

const showMotivationalQuote = config.showMotivationalQuote;

const roxFlags = {
  showMotivationalQuote: new Rox.Flag()
};

const server = http.createServer((req, res) => {
  var message = '<h1>Hello Rollout!</h1>';
  if (showMotivationalQuote) {
    message += "<h3>The number zero. It's whatever you make of it.</h3>"
  }

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html');
  res.end(message);
});

server.listen(port, hostname, () => {
  Rox.register("roxFlags", roxFlags);
  Rox.setup(config.roxAPIKey);
  console.log(`Server running at http://${hostname}:${port}/`);
});

Run the application and go back to the CloudBees Feature Management administration screen. You'll see that our feature flag was created and enabled on the server. Just like that, we've created our first feature flag with CloudBees Feature Management. How cool is that?

In order to make this feature flag useful, we need to add it to an experiment. CloudBees Feature Management manages feature flags through the concept of "Experiments." Refer to the CloudBees Feature Management documentation for more information on experiments. For now, click on the "Production" link, as shown in the above screenshot. Next, click the "Experiments" link from the dropdown menu.

Clicking on this will bring you to a screen with a “Create an Experiment” button. Go ahead and click that and fill out the new experiment window.

Fill out the "New Experiment" window with sensible information. Then click the "Set Audience" button and take a look at our next screen.

Now we can start doing some interesting things!

Controlling the Feature Flag

Go back to your app.js file. We’ve set up the Rox client, but we're still using the old variable to manage our motivational message. We can check the status of a feature flag through the isEnabled() method on the flag.

Update your app.js file to remove the hardcoded flag and use the CloudBees Feature Management feature flag instead.

const http = require('http');
const Rox = require("rox-node");

const config = require('./config.json');

const hostname = '127.0.0.1';
const port = 3000;

const roxFlags = {
  showMotivationalQuote: new Rox.Flag()
};

const server = http.createServer((req, res) => {
  var message = '<h1>Hello Rollout!</h1>';
  if (roxFlags.showMotivationalQuote.isEnabled()) {
    message += "<h3>The number zero. It's whatever you make of it.</h3>"
  }

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html');
  res.end(message);
});

server.listen(port, hostname, () => {
  Rox.register("roxFlags", roxFlags);
  Rox.setup(config.roxAPIKey);
  console.log(`Server running at http://${hostname}:${port}/`);
});

While we're here, let's update our config.json file to remove the showMotivationalQuote setting.

{
  "roxAPIKey": "5a68f9d5f807d908a1426405"
}

Run your application and check it out in the browser. You'll see that our motivational message isn't displaying. What broke? Well, nothing. It's just that CloudBees Feature Management defaults all new feature flags to false. This can be changed when creating the flag, though.

So let’s go back to the administration screen and update our feature flag by setting it to true. Don’t forget to click the “Update Audience” button, as it doesn’t save the changes automatically.

Now run your application. You'll see the motivational message controlled through the use of CloudBees Feature Management’s feature flag service.

Congratulations! You've just implemented your first feature flag as a service. Now you have the ability to change the behavior of your application---all without touching configuration files or environment variables and without deploying new code. We can now gradually roll out new features to our user base or perform split testing between groups of users.

We can also change the percentage of what users will see our new message. Go back to the "Experiments" screen with our feature flag and change it from true to split. When set to split, the interface allows us to change the percentage set to true. Here I set it to true for 30% of all users.

Now run our application and delight in the non-determinism of whether or not we'll see our motivational message.

Issues With Feature Flags

Feature flags are awesome. They enable us to run experiments in production. However, there are some risks to using feature flags. First, you can't just use one feature flag. Implement one, and another will follow. Go ahead and try to just give the marketing department a single one. Lest we get ourselves into trouble with technical debt, feature flags need management. They also need to have a retirement plan.

You Take It From Here

You can do even more with feature flags. Check out the CloudBees Feature Management documentation for more information on additional options and customizability. And the documentation also contains additional information on the management console.

Feature flags are a powerful tool. They allow you to respond quickly to market trends and perform complicated A/B split testing---all without updating or redeploying code. But you should keep it in mind that your feature flags need to be managed, and that management must be simple, effective, and centralized.

This post was written by Casey Dunham.  Casey, who recently launched his own security business, is known for his unique approaches to all areas of application security, stemming from his 10+ year career as a professional software developer. His strengths include secure SDLC development consulting; threat modeling; developer training; and auditing web, mobile, and desktop applications for security flaws.

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.