DEV Community

Adam Gardner
Adam Gardner

Posted on • Updated on

Add Feature Flags with only a JSON flat file in NodeJS

Did you know that it is possible to add feature flags to your application simply by adding a JSON file containing the flags? Read on and I'll show you how to do it in NodeJS.

Zero restarts or downtime, just a fully dynamic application, all by using a JSON file.

Start without Feature Flags

Start the tutorial at the beginning, with a simple node app that has no feature flagging capabilities.

The app is a simple HTTP server which renders a static HTML page but, while rendering, replaces the placeholder text BACKGROUND-COLOUR with the hex value returned from getBackgroundColour().

Create a package.json file like this:

{
    "name": "MyApp",
    "version": "0.1.0",
    "private": true,
    "dependencies": {
    },
    "scripts": {
      "start": "node app.js"
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, create app.js like this:

var fs = require('fs');
var http = require('http');
var port = 9123;
var html = fs.readFileSync('index.html').toString();

var getBackgroundColour = async function() {
    return "#AAEEDD";
}

// ===========================================================
// This is our main HttpServer Handler
// ===========================================================
var server = http.createServer(async function (req, res) {
  res.writeHead(200, 'OK', {'Content-Type': 'text/html'});

  // replace background colour placeholder with real hex value
  var finalHtml = html.replace(/BACKGROUND-COLOUR/g, await getBackgroundColour());
        res.write(finalHtml);
        res.end();
});

// Listen on port 9123, IP defaults to 127.0.0.1
server.listen(port);

// Put a friendly message on the terminal
console.log('Server running at http://127.0.0.1:' + port + '/');
console.log("Info", "Service is up and running - feed me with data!");
Enter fullscreen mode Exit fullscreen mode

Finally, create a file called index.html with this content:

<html>
  <head>
    <title>Demo</title>
    <style>
      body {background-color: BACKGROUND-COLOUR };
    </style>
  </head>
  <body>
    <div style="text-align:center">
    <h1>A Demo Page...</h1>
    <p>This is a simple demo page. The background colour is: BACKGROUND-COLOUR</p>
    </div>
    </div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Run Simple App

Run the app with npm run start and you should see this:

> MyApp@0.1.0 start
> node app.js

Server running at http://127.0.0.1:9123/
Info Service is up and running - feed me with data!
Enter fullscreen mode Exit fullscreen mode

Basic Application

Try changing the hex value from "#AAEEDD" to any other valid hex value. It will work, but you will need to restart the application to refresh the colour.

In today's world, restarts and / or redeployments often equal downtime. Downtime is the enemy!

Let's fix the app so we can change colour without any restarts or downtime.

Add Feature Flags

It is time to add feature flags to your application. Create a file called flags.json and add this content:

{
    "$schema": "https://flagd.dev/schema/v0/flags.json",
    "flags": {
      "background-colour": {
        "state": "ENABLED",
        "variants": {
          "white": "#D6D4D2",
          "green": "#73A53E",
          "orange": "#FF7C00",
          "lime": "#D3D309",
          "blue": "#4AB9D9"
        },
        "defaultVariant": "green"
     }
    }
}
Enter fullscreen mode Exit fullscreen mode

Replace app.js with this content (notice the new OpenFeature code and modified getBackgroundColour implementation):

var fs = require('fs');
var http = require('http');
var port = 9123;
var html = fs.readFileSync('index.html').toString();
var {OpenFeature} = require("@openfeature/server-sdk")
var {FlagdProvider} = require("@openfeature/flagd-provider")

var getBackgroundColor = async function() {

  const backgroundColour = await client.getStringValue("background-colour", "#FFFFFF", { });

  return backgroundColour;
}

// OpenFeature Initialisation Code
// Register your feature flag provider
OpenFeature.setProvider(new FlagdProvider());
// create a new client
const client = OpenFeature.getClient();

// =======================================================
// This is our main HttpServer Handler
// =======================================================
var server = http.createServer(async function (req, res) {
  res.writeHead(200, 'OK', {'Content-Type': 'text/html'});

  // replace background colour placeholder with real hex value
  var finalHtml = html.replace(/BACKGROUND-COLOUR/g, await getBackgroundColor());
  res.write(finalHtml);
  res.end();
});

// Listen on port 9123, IP defaults to 127.0.0.1
server.listen(port);

// Put a friendly message on the terminal
console.log('Server running at http://127.0.0.1:' + port + '/');
console.log("Info", "Service is up and running - feed me with data!");
Enter fullscreen mode Exit fullscreen mode

Instead of returning a static value, the code above uses OpenFeature and the Flagd provider (feature flag backend) to retrieve flag values (we will see how flagd reads the JSON file soon). If the feature flag system is offline or unavailable for whatever reason, a safe fallback value of #FFFFFF is returned.

Now modify package.json to look like the code below:

{
    "name": "MyApp",
    "version": "0.2.0",
    "private": true,
    "dependencies": {
      "@openfeature/flagd-provider": "^0.12.0",
      "@openfeature/server-sdk": "^1.13.2"
    },
    "scripts": {
      "start": "node app.js",
      "dev_linux": "export FLAGD_RESOLVER=in-process && export FLAGD_OFFLINE_FLAG_SOURCE_PATH=flags.json && node app.js",
      "dev_windows": "set FLAGD_RESOLVER=in-process&&FLAGD_OFFLINE_FLAG_SOURCE_PATH=flags.json&&node app.js"
    }
}
Enter fullscreen mode Exit fullscreen mode

The only difference is that we've added dependencies for OpenFeature and flagd. We also have 2 additional scripts, which set some important environment variables: FLAGD_RESOLVER and FLAGD_OFFLINE_FLAG_SOURCE_PATH. This is how flagd knows to read from a local file. The in-process resolver is the "magic" that means you have a feature flag system "inside" your code logic.

Run Feature Flag Enabled App

Save the files and install the dependencies by running npm install once.

Fire up the app by running the appropriate command (depending on your OS): npm run dev_linux (for linux and macos) or npm run dev_windows (for windows).

You should see:

% npm run dev_linux

> MyApp@0.2.0 dev_linux
> export FLAGD_RESOLVER=in-process && export FLAGD_OFFLINE_FLAG_SOURCE_PATH=flags.json && node app.js

Server running at http://127.0.0.1:9123/
Info Service is up and running - feed me with data!
Enter fullscreen mode Exit fullscreen mode

Feature flag enabled application

The application is green because the defaultVariant in flags.json is green which maps to the hex value "#73A53E".

Change App Colour at Runtime

Change the defaultVariant in flags.json to a different variant key such as orange. Save flags.json and refresh the browser window (do not restart the node app).

You should see this:

App is Orange

Add Another Colour Choice at Runtime

Leave the node app running and add a new Key: Value pair to the variants block in flags.json. Set the defaultVariant to your new Key and refresh the browser page.

For example, I'm adding a new red variant:

{
    "$schema": "https://flagd.dev/schema/v0/flags.json",
    "flags": {
      "background-colour": {
        "state": "ENABLED",
        "variants": {
          "white": "#D6D4D2",
          "green": "#73A53E",
          "orange": "#FF7C00",
          "lime": "#D3D309",
          "blue": "#4AB9D9",
          "red": "#FF0000"
        },
        "defaultVariant": "red"
     }
    }
}
Enter fullscreen mode Exit fullscreen mode

Colour Variant Added at Runtime

Migrate to another Feature Flag Vendor

One of the powers of OpenFeature is that it allows you to easily migrate to different vendors.

For example, migrating to DevCycle (or any other provider) is almost just a case of replacing references to flagd with devcycle:

OpenFeature.setProvider(new FlagdProvider());
Enter fullscreen mode Exit fullscreen mode

to

OpenFeature.setProvider(devcycleClient.getOpenFeatureProvider());
Enter fullscreen mode Exit fullscreen mode

All compatible feature flag vendors and integration instructions can be found on the OpenFeature ecosystem page.

See part two to add runtime contextual awareness to your feature flagging.

🎉 Happy Feature Flagging! 🎉

Top comments (0)