Node.js feature flags: a practical getting started guide with Express.js example

By Geshan Manandhar on October 29, 2021

Feature flags help us turn a feature on or off without the need to deploy any code. Node.js feature flags also enable the same. In this post, we will discuss a practical example in a Node.js express application to show and hide some footer icons depending on a Flagsmith feature flag. Let’s get rolling!

Node.js Feature Flag Illustration

Prerequisites

Before jumping to the code, below are some of the things that would be great to know:

  1. Prior general knowledge of Node.js and express would be expected.
  2. Some experience of NPM and how to add new packages with NPM would be needed.
  3. Any knowledge of Git and GitHub would be really helpful.
  4. A Flagsmith account will be used to create a feature flag so register now if you have not. There is a free plan available.
  5. You will need a Github account to clone the demo repository and deploy code to Railway.app

In the next section, we will elaborate on the example Express.js application we will use for this practical getting started guide. Time to dive into the code:

Example: Express single-page application for Node.js feature flags

For this hands-on getting started guide, we will use a pre-built Express application. This Node.js application was built as a demo to build a single-page website. This step-by-step tutorial explains how to build a single-page website with Express.js and Pug as the templating library. The demo website looks like the below:

Eventually Podcast webpage

It is a mock coming soon page with a form to get the email address which doesn’t really collect the email addresses at this point. 

The goal here is to show/hide the 4 icons; Twitter, Instagram, GitHub, and Email, using a feature flag. We will create the feature flag on Flagsmith and use Node.js to show or hide those icons depending on whether the feature flag is enabled or not. Hypothetically we want the user to focus on giving us their email address vs. being diverted to the social channels. But if we change our minds we want to be able to get the icons in the footer back without the need to deploy any code.

After doing the code change we will deploy the application to Railway.app free plan. In the following section, we will set up the feature flag on Flagsmith.

Set up feature flag on Flagsmith

To create a feature flag for toggling the visibility of the social icons on the footer, we will first need to create a project after logging into Flagsmith. This can be done by clicking the “Create Project” button found at the top right side of the Projects page as seen below:

Video of creating a new feature in Flagsmith

I have named id `eventually-podcast` but it is up to you to name it as you like. The project page looks like the following after the project is successfully created:

Flagsmith project page

Now we will create a new feature flag hitting the “Create Your First Feature” button and fill in the details as follows:

Screenshot of creating a feature

So, we will create our first feature flag named `show_footer_icons` which is enabled by default, has no value, and we have added a description to help us know in the future what it is used for. It will look like the following when the feature flag is created:

Creating a feature flag to show footer icons in the Flagsmith application

Note that we have 2 environments, development and production. We will make use of them later. In the next section, we will clone (or fork) the open source Node.js express application and add Flagsmith Node.js SDK to it to use the feature flag we just created as part of this Node.js feature flags tutorial.

Install Flagsmith Node.js client

To proceed further with this tutorial you can clone the open-source repository that has the “Eventually Podcast” landing page running. To do that we will need to run the following command:

1git clone git@github.com:geshan/nodejs-express-tutorial.git

Then we will go into the directory, install the NPM dependencies and run the app to test by running:

1cd nodejs-express-tutorial
2npm install
3npm start

It will start the server on the master branch and if you go to http://localhost:3000 in your favorite browser you will see something like the below:

Eventually Podcast homepage (with icons)

Consequently, you can stop the server and install two new NPM packages with:

1npm i --save flagsmith-nodejs node-cache

It will result in something like below:

1npm WARN eventually-podcast@1.0.0 No repository field.
2
3+ node-cache@5.1.2
4+ flagsmith-nodejs@1.1.0
5updated 2 packages and audited 98 packages in 1.845s
6
76 packages are looking for funding
8  run `npm fund` for details
9
10found 0 vulnerabilities

So at this point, we have installed flagsmith-nodejs (the Flagsmith Node.js SDK), and node-cache NPM modules. The Flagsmith Node.js SDK is used to communicate with the Flagsmith API from our Node.js application. The node-cache NPM module is installed to keep the communication smoother with fewer calls to the Flagsmith API. In the next section, we will see how we can integrate the feature flag we have created with our code.

Use Flagsmith Node.js client with some caching

To show or hide the social icons on the footer of our mock podcast’s landing page after installing the needed NPM modules, we will change the `index.js` on the root of the project to look like below:

1const express = require('express');
2const path = require('path');
3const flagsmith = require('flagsmith-nodejs');
4const nodecache = require('node-cache');
5
6const app = express();
7const port = process.env.PORT || '3000';
8
9app.set('views', path.join(__dirname, 'views'));
10app.set('view engine', 'pug');
11app.use(express.static(path.join(__dirname, 'public')));
12
13flagsmith.init({
14  environmentID: process.env.FLAGSMITH_ENVIRONMENT_ID || 'LKfXyih5yqZxL3huZ5LraP',
15  cache: new nodecache({stdTTL : 10, checkperiod: 10}),
16});
17
18app.get('/', async (req, res) => {
19  let showFooterIcons = false;
20  try {
21    showFooterIcons = await flagsmith.hasFeature('show_footer_icons');
22  } catch (e) {
23    console.log(`Error connecting to flagsmith - ${e.getMessage} `, e);
24  }
25
26  console.log(`show footer icons: ${showFooterIcons}`);
27  res.render(
28    'index', 
29    { 
30      title: 'Coming Soon!', 
31      mainText: 'Eventually Podcast', 
32      subText: `Drop your email address below and we will let you know when we launch the Eventually podcast. 
33      <br>Brought to you by amazing people`,
34      showFooterIcons,
35    }
36  );
37});
38
39app.listen(port, () => {
40  console.log(`Listening to requests on http://localhost:${port}`);
41});

The main changes here are that we have required the flagsmith SDK and the node cache module. Just above the `app.get` route definition line, we are initializing the flagsmith SDK with the environment ID. To make it more efficient we are also using Node cache. The cache is set to 10 seconds, meaning it will reuse the values from the flagsmith API for 10 seconds before making another call. 

The main part here is the `environmentID` that we can get from the `Initialising your project` section of the feature flag we created in the previous step as seen below:

Flagsmith application highlighting the environmentID

For testing and development, we will use the Development environment, we will use the Production environment when we deploy our application. The environment ID is pulled in from the process.env. If the “FLAGSMITH_ENVIRONMENT_ID” is not available as an environment variable then the given string is used. We have a fallback string in place so that even if the environment ID is not set that application will still work.

The other change is in the route definition, where we initialize a variable named `showFooterIcons` as false. So if we hit an issue while contacting Flagsmith we will not show the footer icons. Then we try to get if the feature is available on Flasmith with:

1showFooterIcons = await flagsmith.hasFeature('show_footer_icons');

If the feature is enabled it will return a true else it will be false, this is wrapped in a try catch to make it more resilient. After that the `showFooterIcons` variable is passed on to the “index” view in the res.render call.

The view file located at `views/index.pug` looks like the following to make the rendering of the social icon in the footer dynamic as follows:

1extends layout
2
3block body-content
4  // Header
5  header#header
6    h1 #{mainText} 
7    p
8      | !{subText}
9  // Signup Form
10  form#signup-form(method='post' action='#')
11    input#email(type='email' name='email' placeholder='Email Address')
12    input(type='submit' value='Sign Up')
13  // Footer
14  footer#footer
15    if showFooterIcons
16      ul.icons
17        li
18          a.icon.brands.fa-twitter(href='#')
19            span.label Twitter
20        li
21          a.icon.brands.fa-instagram(href='#')
22            span.label Instagram
23        li
24          a.icon.brands.fa-github(href='#')
25            span.label GitHub
26        li
27          a.icon.fa-envelope(href='#')
28            span.label Email
29    ul.copyright
30      li &copy; Untitled.
31      li
32        | Credits: 
33        a(href='http://html5up.net') HTML5 UP
34  // Scripts
35  script(src='/assets/js/main.js')

The main change here is in the `footer#footer` section, where the `ul.icon` list is only rendered if the `showFooterIcons` is true which depends on the feature flag we created in the previous step being enabled. All the above code changes are available as a pull request for your reference. In the next section, we will do a quick test of the above changes.

Test Node.js feature flag

To test our change we will re-run the application with:

1npm start

And then hit the browser again at http://localhost:3000, it will render an output like:

Eventually Podcast webpage (with icons)

Next up we will first turn off the feature flag on the Flagsmith interface and it will look like:

Flagsmith application with show footer icons toggle off

After turning off the flag and confirming changes, if we refresh our application it will not show the footer icons and look like the below:

Eventually Podcast webpage (without icons)

Great! The footer icons are gone and to bring them back it is as easy as turning on the flag, waiting for 10 seconds for the cache lifetime, and hitting refresh. As the next step we will deploy our application to Railway.app, a simple and elegant way to deploy a Node.js app or any dockerized application.

Deploy to Railway.app 

Railway.app has a free plan to deploy applications for up to $5 a month. To deploy the application to Railway we will first install the railway CLI, to do this we will run:

1npm i -g @railway/cli

This will install the railway CLI and will result in the following:

1npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
2npm WARN deprecated har-validator@5.1.5: this library is no longer supported
3> @railway/cli@0.2.45 preuninstall /some/path/node_modules/@railway/cli
4> go-npm uninstall
5> @railway/cli@0.2.45 postinstall /some/path/node_modules/@railway/cli
6> go-npm install
7Downloading from URL: https://github.com/railwayapp/cli/releases/download/v0.2.45/railway_0.2.45_darwin_amd64.tar.gz
8+ @railway/cli@0.2.45
9updated 1 package in 10.009s

Now as we have the railway CLI we will log in to Railway using Github, to do this we will run the following on the project root:

1railway login

It will first ask us to open the browser and take us to the login screen like below:

Railway app login

After we click GitHub and authenticate, it will confirm the authentication as follows:

Railway app login success page

The CLI will look like the below after the authentication is successful:

1Press Enter to open the browser (^C to quit)
2🚝 Logging in... No dice? Try railway login --browserless
3🚊 Logging in... 
4🎉 Logged in as Your Name (<email>@domain.com)

Subsequently, we can initialize the project on Railway with:

1railway init

It will ask if we want a starter or an empty project, just select “Empty Project” and give the project a name as follows:

1✔ Starting Point: Empty Project 
2✔ Enter project name: eventually-podcast
3✔ Environment: production
4🎉 Created project eventually-podcast

It will also open the browser to show us the project settings as follows:

Eventually Podcast installed in the Railway app

Here we will add our `FLAGSMITH_ENVIRONMENT_ID` environment variable from the production environment:

Flagsmith application highlighting the environmentID

We will add it to the variables page of Railway’s project as follows since we want to use the Production environment not the Development one for Railway:

Railway app with the Flagsmith environmentID shown

To save the variable we will click “Add”.

The next step in the process to deploy is to run:

1railway up

As the application is already dockerized it will build the docker image and deploy the application. If it was not dockerized, Railway.app would detect that it is a Node.js application and deploy it using the build pack. If you are interested, please do check out their docs.

After the deployment is finished it will show us something like the below:

1☁️ Build logs available at https://railway.app/project/c4fc117f-d04e-4a61-9788-6216fb5e5596/deployments?id=d43a6572-d418-4506-ae88-c0ce678f5c4d
2 
3==========================
4Using detected Dockerfile!
5==========================
6Sending build context to Docker daemon  3.394MB
7Step 1/9 : FROM node:14-alpine as base
8 ---> fe39f43f1d22
9Step 2/9 : WORKDIR /src
10 
11 ---> Running in 6bf8c2cd1fa1
12Removing intermediate container 6bf8c2cd1fa1
13 ---> 96ae9ce24569
14Step 3/9 : COPY package*.json ./
15 ---> 82cd9bd661a7
16Step 4/9 : EXPOSE 3000
17 ---> Running in 89bacb0b1d43
18Removing intermediate container 89bacb0b1d43
19 ---> 0dfd09862db1
20Step 5/9 : FROM base as production
21 ---> 0dfd09862db1
22Step 6/9 : ENV NODE_ENV=production
23 ---> Running in d4ab473be240
24Removing intermediate container d4ab473be240
25 ---> bbc350ac53b9
26Step 7/9 : RUN npm ci
27 ---> Running in 2970f701628c
28added 98 packages in 1.454s
29Removing intermediate container 2970f701628c
30 ---> 99b660ab4187
31Step 8/9 : COPY . ./
32 ---> 036d470ec912
33Step 9/9 : CMD ["node", "index.js"]
34 ---> Running in 380cf6634c1c
35Removing intermediate container 380cf6634c1c
36 ---> 6e5936efd6d4
37[Warning] One or more build-args [FLAGSMITH_ENVIRONMENT_ID RAILWAY_ENVIRONMENT RAILWAY_STATIC_URL] were not consumed
38Successfully built 6e5936efd6d4
39Successfully tagged registry.railway.app/d43a6572-d418-4506-ae88-c0ce678f5c4d:latest
40======= Build Completed ======
41Waiting for Deploy To Finish
42☁️ Deployment logs available at https://railway.app/project/c4fc117f-d04e-4a61-9788-6216fb5e5596/deployments?id=d43a6572-d418-4506-ae88-c0ce678f5c4d
43OR run `railway logs` to tail them here
44☁️ Deployment live at https://eventaully-podcast-production.up.railway.app

We will see the URL for our project that we can hit on the browser of our choice. Now when we go to that URL we should see our application working as follows:

Eventually Podcast homepage (with icons)

To test the feature flag, we will turn off the flag on the Production environment and see that the footer icons disappear:

Flagsmith application with toggle off for footer icons

And it does work:

Eventually Podcast homepage (without icons)

I will leave the feature flag on. If you want to play around with the Node.js feature flags demo link, here it is. Depending on what you want to do Railway.app free plan also supports custom domain. If you want you can also use the GUI version of Railway.app and connect your own fork of the repository. With their GitHub integration, we can have automatic deployments configured by the git branch.

Conclusion

In this guide, we saw how we can implement Node.js feature flags in a relatively simple Express.js application. The use of node-cache, helped us cache the feature flag’s value locally for 10 seconds. We also learned about using an environment variable to store the environment ID which made it a lot easier to switch between development and production environment when deploying the app on Railway.app. Don’t forget to use the best open-source feature flag product, Flagsmith, for your next Node.js project. Flagsmith also has on-premise and cloud-hosted solutions with great pricing plans.