DEV Community

loading...

Runtime Config for Frontend applications using TwoFlags

efreyreg profile image Ernesto Freyre Originally published at Medium on ・8 min read

https://www.pexels.com/photo/white-and-black-music-mixer-164746/

Cloudflare Workers had become my favorite Serverless platform. The introduction of Workers KV, a storage service, added a huge potential for new applications. One of this applications is Feature Flags.

What are Feature Flags?

Feature Flags or feature toggle is a software engineering technique to hide, enable or disable features on applications during runtime. Is also used to provide runtime configurations to applications making them environment agnostic.

Disabling, Enabling or Enhancing features is achieved by toggling a boolean Flag, false by default, once we want to enable it we don’t need to go back to the code change the logic and redeploy our application. Centralizing this decisions with all the management overhead already taken care for you.

We can also progressively enable certain features for a percentage of users or directly allow some users to have the feature enabled via an override.

Feature Flags are also a good way to abstract runtime configurations, anything that an Application needs to work, it could be API Urls, Client IDs, Integration IDs, like Maps Keys, Logging Keys, etc. noting that this are not secret keys, since everything on the Client by definition is public. With a dynamic runtime configuration setting using an external Feature Flags service your Application builds are totally environment agnostic. This means you only need to build your applications once and then use the build artifacts to deploy on any environment vs. having to build your application for that particular environment.

Commercial solutions

There are several commercial solutions that provide great features and support. One problem common to all is the pricing model, ranging from expensive to limiting in terms of active monthly users.

This limits adoption of Feature Flags on smaller projects, projects where cost could be a factor or where usage surpass the limits imposed by the subscription plans available.

For this reasons we build TwoFlags, an OpenSource Feature Flags service built on top of Cloudflare Workers and Workers KV.

TwoFlags

TwoFlags name is inspired in the colloquial phrase “just a couple of …” in this case, “just a couple of flags” Which is a fun way to say, I don’t need more than a couple of flags, so, 2 flags.

The code can be found here:

TwoFlags

It contains:

To deploy your own instance of the TwoFlags API service you will need a Cloudflare (https://www.cloudflare.com/) account with access to Workers service (cost $5/month). Follow the instructions on the API repository.

Once you have your TwoFlags service in place lets create the Flags for our demo.

If you need to test it first send us a message, we can create you a trial account o a demo environment we have for development purposes.

Creating Environment, Namespaces and Flags.

TwoFlags has support for:

  • Environments: Each Environment is the top level organization element. It contains an id, name and origins array. The origins is the set of domains your application will be deployed, this helps the Flag Resolver identify the environment without the Application explicitly doing it (important feature for agnostic frontend builds)
  • Namespaces: Namespaces is the concept that matches your applications. This is for each application on your company you will need a Namespace
  • Flags: Flags are the actual data toggles. You can enable/disable them and they also have a type: string, boolean, number, segment.

Assuming you have a running TwoFlags service running.

Create an Environment: id: local, name: Local, origins: localhost:3000

$ curl -H 'Authorization: Bearer <apikey>' \
-X POST https://api.twoflags.io/environments \
-d '{"id":"local","name":"Local","origins":["localhost:3000"]}'

$ curl -H 'Authorization: Bearer <apikey>' \
https://api.twoflags.io/environments

{"data":[{"id":"local","name":"Local","origins":["localhost:3000"]}],"slots":4}

Create a Namespace: id: frontend, name: Frontend

$ curl -H 'Authorization: Bearer <apikey>' \
-X POST https://api.twoflags.io/namespaces \
-d '{"id":"frontend","name":"Frontend"}'

$ curl -H 'Authorization: Bearer <apikey>' \
https://api.twoflags.io/namespaces

{"data":[{"id":"frontend","name":"Frontend"}],"slots":19}

Create a couple flags (pun intended)

  • maintenance: type: boolean
  • color: type: string
$ curl -H 'Authorization: Bearer <apikey>' \
-X POST https://api.twoflags.io/flags \
-d '{"id":"maintenance","name":"Maintenance", "type":"boolean", "active": true}'

$ curl -H 'Authorization: Bearer <apikey>' \
-X POST https://api.twoflags.io/flags \
-d '{"id":"color","name":"Background Color", "type":"string", "active": true}'

$ curl -H 'Authorization: Bearer <apikey>' \
https://api.twoflags.io/flags

{"data":[{"id":"maintenance","name":"Maintenance","type":"boolean","active":true},{"id":"color","name":"Background Color","type":"string","active":true}],"slots":98}

With the Environment, Namespace and Flags in place we only need to set the values of each flag and query the Resolver.

To query the Resolver we do:

$ curl -H 'origin: [http://localhost:3000/'](http://localhost:3000/') \
'https://resolver.twoflags.io?account=<account>&ns=frontend'

{"flags":{},"environment":"local","namespace":"frontend"}

As you can see our flags object is empty. This is because we haven’t set any of the values in this environment/namespace

Setting the values of flags on environment/namespace

$ curl -H 'Authorization: Bearer <apikey>' \
-X PATCH https://api.twoflags.io/values \
-d '{"id":"maintenance","environment":"local", "namespace":"frontend", "value": false}'

$ curl -H 'Authorization: Bearer <apikey>' \
-X PATCH https://api.twoflags.io/values \
-d '{"id":"color","environment":"local", "namespace":"frontend", "value": "#007DFF"}'

$ curl -H 'origin: [http://localhost:3000/'](http://localhost:3000/') \
'https://resolver.twoflags.io?account=<account>&ns=frontend'

{"flags":{"maintenance":false,"color":"#007DFF"},"environment":"local","namespace":"frontend"}

As you can see the Resolver also returns the detected environment and the namespace.

React Integration.

Using the REST endpoint for the Resolver is documented, you can use it to integrate the Feature Flags service on your frontend or backend applications (as long as it doesn’t contain secret flags)

For React, we already have an integration ready.

twoflags-io/react-featureflags

Let’s use our existing flags on a front end application. For this I will use a template NextJS app (written in TypeScript) with a couple pages.

outsrc/template-frontend

Once cloned or used as base template, we will need to install the React helper component.

$ git clone [https://github.com/outsrc/template-frontend](https://github.com/outsrc/template-frontend) demo
$ cd demo
$ yarn
$ yarn add @twoflags/react-featureflags

Next step is to create an _app.tsx component inside the src/pages folder. This component will wrap all pages so we can inject the Feature Flags provider there and make it available for the rest of the application.

Notice we only need to specify the clientID , the featureFlagsAPI and the namespace. The environment will be resolved to local because we will be running our application on localhost:3000 and thats the origin of the local environment.

This is the feature that makes our application builds environment agnostic. We only have to provide the environments for all the origins that our application will be deployed.

With this in place we can start querying for feature flags on our components.

Time to make some changes to the application to support the feature flags that we have on our namespace. First, maintenance which indicates that our Application is on maintenance mode. For this we will create a HOC to wrap our pages and if maintenance mode is enabled then an overlay message will be displayed.

This component (and style) will read the Feature Flags object, containing the maintenance flag and render an overlay over the whole app if evaluates TRUE.

To use it on our pages we can wrap them like this:

export default **_blockOnMaintenance_** (Index)

This is the result.

Un-setting the flag.

As you can see Flags updates on the page are not real time, the React integration is constantly pooling from the Resolver at a pre-determined time interval (default is 30 seconds). Although if the page captures the focus the flags are resolved in that moment.

This works for all feature flags types we define on our API. Having boolean toggles makes it easier for avoiding users to access some parts of our application.

String flags are mostly used to store integration Keys to third party services, like authentication (Auth0, Okta), logging (Sentry, NewRelic, Loggly), etc. Never use it to store secrets. Anything on the browser or any client is by definition insecure. This is true for TwoFlags or any other Feature Flags service.

Segments

Segments are a special Flag type that takes a numeric value between 0 and 100. (percentage) and resolves only 2 values ‘A’ or ‘B’ based on that threshold value and a correlation ID.

A Correlation ID is a hashed user id. To guarantee a user always see the same value while the threshold value of the segment progresses towards 100. The hash avoids leaking the actual user identification to the service.

This is:

  • Flag value = 0: Always A
  • Flag value = 100: Always B
  • No Correlation ID: Always A
  • Flag value 0 < flag < 100 + Correlation ID: A (100-flag percent), B (flag percent) (Ex. flag=20 + Correlation ID, 80% of users get A, 20% of users get B)

This is useful when we want to enable a feature for a percentage of your users and check conversion on it.

Let’s create a segment flag

$ curl -H 'Authorization: Bearer <apikey>' \
-X POST https://api.twoflags.io/flags \
-d '{"id":"experiment001","name":"Experiment 001", "type":"segment", "active": true}'

Setting the value to 50, so 50% of users will get A and 50% will get B

$ curl -H ‘Authorization: Bearer <apikey>’ \
-X PATCH [https://api.twoflags.io/values](https://api.twoflags.io/values) \
-d ‘{“id”:”experiment001",”environment”:”local”, “namespace”:”frontend”, “value”: 50}’

Let’s change our application to use the experiment001 segment flag. First a new helper component Experiment that will render one of two sub-components based on an experiment’s value.

https://medium.com/media/3e29ccc0ba624e98a00abb91eaed9af4/href

We will be dynamically changing a fictitious user id, for that we will need a unique id generator function. One of my favorites is cuid (https://www.npmjs.com/package/cuid)

$ yarn add cuid

And the changes to our index.tsx page

https://medium.com/media/f345becfafc6c25e547da4f4868bc599/href

Notice the Experiment component tied to the experiment001 flag we just created.

The button at the bottom will create a new random UID and store it to display it and pass it to the uniqueIDUpdater function. This function will trigger a feature flags update so, If the new user id falls into the group B we will see a different message on the page.

This user is seeing variant A

This user is seeing variant B

Once you decide based on logs data what version you want to keep, you can remove the experiment001 flag and the code from the page keeping the winning variant. Overriding variants for users is on the roadmap.

That’s it, We wrote TwoFlags to learn about Cloudflare Workers and Workers KV, taking into account our own needs. We plan to keep improving the code and add more features. You can contribute to the code, let us know, PRs welcome.

Discussion

pic
Editor guide