Feature Flag with GraphQL, ApolloClient and React 🕹

 

 

Feature flags

Feature flag (or feature toggle, feature switch…)

 

The idea is to be able to enable or disable features on the fly.

 

  • A/B testing
  • App configuration
  • Gradually enable features
  • many more

 

Storage

They introduce a certain complexity, but it does not mean you need a third-party service to use feature flag though.

 

  • In memory on the server
  • Application DB
  • Third-party services

 

Tip #1: The right tool for the right job.

GraphQL Schema

// our data
["feature1", "feature2"]


// our schema
type Feature {
  name: String!
}

type Query {
  enabledFeatures: [Feature!]!
}

GraphQL Schema

Tip #2: We expose functionality without exposing implementation details.

 

 







type Feature {
  name: String!
}

type Query {
  enabledFeatures: [Feature!]!
}

Tip #3:

 

React+

Apollo Client

I will be using Apollo Client as I'm familiar with the API, but it does not matter that much library you use.










const client = new ApolloClient({
  uri: "https://7jxju.sse.codesandbox.io/"
});

const App = () => (
  <ApolloProvider client={client}>
    <div className="App">
      <h1>Hello Feature Flag</h1>
    </div>
  </ApolloProvider>
);

export default App;
const QUERY = gql`
  query {
    enabledFeatures {
      name
    }
  }
`;

function ListFeatureFlags() {
  const {
    loading,
    error,
    data
   } = useQuery(QUERY);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <h2>List of features:</h2>
      <ul>
        {data.enabledFeatures.map(feature => (
          <li key={feature.name}>
            <p>{feature.name}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

Listing all feature flags

How can we know if a specific feature is enabled?

useFeatureFlag

We want to know if a certain feature is enabled for the user.

 

We want to use that in multiple components.

 

So let's create a hook. Functions that let you “hook into” React state and lifecycle features from function components. You could use HoC, render props, etc.

function useFeatureFlag(name) {
  const {
    loading,
    error,
    data
  } = useQuery(QUERY);

  if (!data) return { error, loading };

  const enabled = data.enabledFeatures.some(feature => feature.name === name);
  
  const feature = {
    error,
    enabled
  };

  return feature;
}

Switching behaviour

We can render a different component if a certain feature is enabled.

function Menu() {
  const flagName = "feature3";
  const feature = useFeatureFlag(flagName);

  if (feature.enabled === undefined) {
    return <p>Loading {flagName}...</p>;
  }

  if (feature.enabled) {
    return <h2>New Menu - {flagName} is enabled 🚀</h2>;
  }

  return <h2>Regular Menu - {flagName} is disabled 🚫</h2>;
}

If we have only feature1 and feature2 enabled when querying feature3 we should see the disabled message.

 

 

 

 

 

 

 

 

 

Similarly, if we query feature2 or feature1 we should see the enabled message.

Caching

Apollo Client comes by default with an in-memory cache implementation.

 

We could rely on this behaviour.

 

The trade-off here is to slow down the overall load in favour of rendering the  dependents way faster later.

function EnabledFeatures({
  children
}) {
  const {
    loading,
    error
  } = useQuery(QUERY);

  if (loading) {
    return <p>Loading...</p>;
  }
  
  if (error) {
    return <p>Error: {error}</p>;
  }

  return (
    <React.Fragment>
      {children}
    </React.Fragment>
  );
}

What is next?

Tip #4: Start it simple.

 

  • Adding options or variations as a field of Feature.
  • Override defaults on components & hooks.
  • isFeatureEnabled so you do not have to filter it in the client-sides

 


type Feature {
  name: String!
  variations: [String!]!
}

function Component {
  const feature = useFeature(
    "feature2",
    { fetchPolicy: "network-only" }
  );
  
  // ...
}

type Query {
  isFeatureEnabled(name: String!): Boolean!
}

Thanks!

Made with Slides.com