DEV Community

Cover image for Building a Vue.js composable for handling Feature Flags
Jakub Andrzejewski
Jakub Andrzejewski

Posted on

Building a Vue.js composable for handling Feature Flags

I recently had to implement a solution in our project that would enable us to have feature flags. The reason for having these flags was that mainly for streamlining the development process as they allow us to develop features and release them to production but they are hidden until the flag is set to true.

This allows testing more complex features in a proper production environment without actually causing issues to existing users.

If you would like to read more about the concept of feature flags implemented with Firebase Remote Config, I have published an article about this topic https://dev.to/jacobandrewsky/using-firebase-remote-config-for-feature-flags-2ji9

In this blog post however, I will guide you through the process of creating your own composable that will help you manage the feature flags in an easy way.

Enjoy!

Feature Flag solutions

There are actually multiple ways you can do this so I wanted to show you some approaches that I have considered along the way and the final solution that me and my team decided to go with.

Local object

The simplest solution out of all available approaches here is just this:

const flags = {
  'awesome_feature': false
}
Enter fullscreen mode Exit fullscreen mode

This solution certainly works but it is not the best in terms of maintainability and execution (especially if you are not a technical person). It requires a developer to change the value of the flag in the code and publish the changes. Considering the complexity of certain development processess, hiding a feature could take hours while we sometimes need minutes.

Content Management System

Yes, you read it correctly. CMS can also be used for handling feature flags. It is not its primary meaning but several web applications are already using content management systems and in the end, feature flags are just a key/value pairs that can be easily stored in a JSON file that we fetch from CMS.

Feature Flag Software

There is actually several software providers that are focusing primarly on feature flag implementations like LaunchDarkly for example. They come with many interesting features that your business may need so I definitely recommend you to check it out if you have a big project and complex release process - LaunchDarkly could help you with it.

But in our case, we decided to go with Firebase Remote Config thanks to its simplicity and easiness of usage.

Vue Composable

The implementation of the Vue composable that is responsible for handling feature flags will not differ that much depending on the path that you will choose in the previous point. The only crucial point is to choose if you should be fetching these flags directly from the frontend or maybe from your own server or middleware, for example:

Frontend -> CMS/FeatureFlagSoftware

or

Frontend -> Backend -> CMS/FeatureFlagSoftware
Enter fullscreen mode Exit fullscreen mode

I personally prefer to have my own backend or middleware because then I can have full control over the flag invalidation process. I explained this issue here https://dev.to/jacobandrewsky/using-firebase-remote-config-for-feature-flags-2ji9

But coming back to the composable itself. What do we need in that composable?

First of all, it would be beneficial to have a type where we could store all our available feature flag names and then a type for key/boolean flag record:

type FeatureFlagName =
  | 'awesome_feature'
  | 'another_awesome_feature';

export type FeatureFlags = Record<FeatureFlagName, boolean>;
Enter fullscreen mode Exit fullscreen mode

Then, we would need a Ref variable where we will be storing the flags and that will be easily available for usage across our application. As feature flags are global, I like to put this variable outside of scope of the composable itself so that components can share this state:

const flags = ref<FeatureFlags>({} as FeatureFlags);
Enter fullscreen mode Exit fullscreen mode

And finally, the composable itself. There are two approaches here in terms of fetching the actual flags that are based purely on the Developer Experience. In my example useApiClient is a composable that is a wrapper around VueUse useAxios but you can use any other data fetching approach.

  1. Fetching flags when useFeatureFlags composable is called
export const useFeatureFlags = () => {
  const { execute, data } = useApiClient<FeatureFlags>();

  onMounted(async () => {
    await execute('/flags');
    flags.value = data.value ? data.value : ({} as FeatureFlags);
  });

  return flags;
};
Enter fullscreen mode Exit fullscreen mode

And then in the component use it like following:

<script setup lang="ts">
const featureFlags = useFeatureFlags();
</script>

<template>
  <AwesomeFeature v-if="featureFlags.awesome_feature" />
</template>
Enter fullscreen mode Exit fullscreen mode
  1. Fetching flags when fetch method from useFeatureFlags composable when needed
export const useFeatureFlags = () => {
  const { execute, data } = useApiClient<FeatureFlags>();

  const fetch = () => {
    await execute('/flags');
    flags.value = data.value ? data.value : ({} as FeatureFlags);

    return flags.value
  }

  return {
    fetch
    flags
  };
};
Enter fullscreen mode Exit fullscreen mode

And then, in the component use it like following:

<script setup lang="ts">
const { flags, fetch } = useFeatureFlags();

onMounted(async () => {
  await fetch('/flags');
});
</script>

<template>
  <AwesomeFeature v-if="flags.awesome_feature" />
</template>
Enter fullscreen mode Exit fullscreen mode

The first approach requires developer to do less steps but also gives him less control over how these flags are fetched. I personally prefer this approach as I am lazy and if I can have similar effect with less steps, I will choose this approach :D

Bonus

To not stress your backend/middleware or the third party service with too many requests for flags (and taking into account that these flags have a tendency to not change that often) you can implement a caching solution along with a timeout that will send a request for flags at most once per five minutes.

Summary

Nicely done! You have just learned how to build a custom Vue composable to handle feature flags. You understand that there are (at least) three approaches in how these flags can be managed and two approaches in how these flags can be used in the code. Hope this will help you manage your feature flags and have safe release process.

Take care and see you next time!

Top comments (7)

Collapse
 
cmacu profile image
Stasi Vladimirov

Isn’t it better to ‘return early’ here?
In other words, checking if the feature is enabled inside the feature component and returning nothing in case it’s not. This helps with 2 things:

  1. DRY - when the feature is included in multiple places we don’t have to repeat the above code block. even more so if that involves some type of a placeholder

  2. It ensures that other developers using the feature would not forget to include the check

Of course an even better approach in large applications is to setup a FeatureFactory component that takes care of all this stuff at a higher level, but assuming you are aiming for a more simplistic approach I think doing the check within the component is the better solution.

Collapse
 
jacobandrewsky profile image
Jakub Andrzejewski

Hey,

Good question and idea as well. The reasoning why I went with a composable is purely because the feature you may want to hide behind a feature flag may not be in one place only. Maybe it is a certain page, a link to a page, router declaration, and some more and this will mean that you will need to call your api multiple times from these places to get the value of the actual flag. and thanks to the approach that I have created here, you can have it in one place (composable) and reuse global state with global ref :)

Collapse
 
cmacu profile image
Stasi Vladimirov • Edited

Maybe you miss understood me, I didn’t say to skip the composable. I think that’s a great idea/design. What I was referring to is where the composable is used and how the flag is checked. Instead of importing and invoking the composable in the parent component, and after checking the flag with v-if. I think it might be a good idea to do all that within the Feature component. Then you either encapsulate the rest of the logic/functionality in a subcomponent or just ignore it whenever the flag is false depending on the feature complexity. This way parent components don’t ever need to worry about the flag, they just import the feature wherever it’s supposed to be. And if the flag is not needed anymore or needs to be modified you just remove it from one place instead of searching for all parent components that deal with it. That’s the DRY benefit.
The biggest benefit is that anyone (including you) using that feature component anywhere doesn’t need to worry about the security or implications of the feature being enabled or not and how to check for that. This encapsulates the feature on its own and everything related to it including the flag composable.
The only gotcha about all this is that your feature component has to be either async and you need to use some of the before mounted hooks to fetch the flags to prevent anything from being rendered if the flag is disabled.
Regarding links/buttons etc leading to that feature, I think they are best handled via FeatureFactory which would ensure that the fetch happens only once and all the logic is applied on consistent way. That’s where you would use the flag composable. Especially if you have a lot of these feature flags and they drive things like menus/workflows/etc. The chances are that there is more than just a feature flag to be checked. Things like authorization, transaction status, timing, ownership and other details/composables are also often relevant so in my mind you would likely need that Link/Button factory/wrapper regardless. Any other approach sounds like a lot of copy/pasting and constant refactoring.

Collapse
 
lmarcinkowski profile image
Łukasz Marcinkowski • Edited

Instead of calling fetch on every onMounted component, maybe a better approach is to create global route middleware for client-only and fetch only once if global featureFlags is not set?
Or run it only once on onMounted in main App component.
I.e in Nuxt:
feature-flags.global.ts -> check process.client -> check featureFlags -> fetch if empty

Collapse
 
jacobandrewsky profile image
Jakub Andrzejewski

That could work as well. But my idea in having this onMounted call for flags was primarly about when you make some change in the Feature Flag software (i.e. Firebase Remote Config) without changing the frontend app (without deploying).

In your case, you would fetch the flags once and you couldnt observe the changes that should be applied after changing the flags.

It really depends on the use case I think (so your solution is correct as well :))

In here, you could also implement some polling mechanism that would be sending a request once per minute for example to see if the flags have changed.

Collapse
 
gabrielgomeso profile image
Gabriel Gomes de Oliveira

I know that I will use this eventually, so thanks! Very nice post

Collapse
 
jacobandrewsky profile image
Jakub Andrzejewski

Glad you liked it!