Using LaunchDarkly Feature Flags With GraphQL Applications featured image

In recent weeks, I’ve been experimenting with different ways to incorporate feature flags into my Kubernetes and microservice environments. One of the things we discuss a lot here at LaunchDarkly is that feature flags and feature management go beyond frontend development. Feature flags have a place in multiple layers of your application and, as you migrate toward microservice architectures, should be distributed across different interaction points. 

Having worked with Kubernetes for a while now, I’ve become familiar with a fairly popular tool called GraphQL. If you haven’t heard of GraphQL, it’s a query language for handling data retrieval from your various APIs and datasources. 

What’s really interesting about GraphQL is that it has the ability to simplify what queries look like, handles subfields more gracefully, and is often much more dynamic than using methods like REST. Here’s an example from the GraphQL documentation of a query that leverages variable inputs, GraphQL above and the corresponding JSON object that gets returned below:

{
  hero {
	name
	appearsIn
	  }
}
{
  "data": {
	"hero": {
  	"name": "R2-D2",
  	"appearsIn": [
    	"NEWHOPE",
    	"EMPIRE",
    	"JEDI"
  	],
  	"friends": [
    	{
      	"name": "Luke Skywalker"
    	},
    	{
      	"name": "Han Solo"
    	},
    	{
      	"name": "Leia Organa"
    	}
  	]
	}
  }
}

For this example, the GraphQL team is pulling from a data source with relational data centered around the main hero of "Star Wars," R2-D2. The query is providing us with the name of the hero, the films they appeared in, and the associated friends. It’s a powerful tool that is a lot of fun to play around with.

But this isn’t a blog post about GraphQL, for that I recommend going through their documentation and examples. Instead, this post is about how we can start to layer in feature flags alongside a GraphQL server to make it more dynamic for developers.

Changing tables with server-side flags

GraphQL really excels at being able to retrieve both specific data points and the relational elements associated with that data. In the example above, the query is centered around R2-D2 being the hero of "Star Wars," but what if we wanted a different hero or a different movie? In that case, we might opt to construct a new table within our database that provides different data points and relationships. 

For instance, let’s say I would prefer to query information about "Lord of the Rings" instead (maybe an unpopular opinion, but hobbits and orcs always captured my interests more than droids and stormtroopers). Let’s start with this premise to begin playing with adding feature flags. 

Before highlighting where we can tie in our feature flags, let’s break down a typical structure of a GraphQL application. In most cases it’s going to consist of three components: 

  • a database for data retrieval (ex: a Postgres or MongoDB database)
  • a server for resolving queries (ex: Go or Node.js express server) 
  • a frontend that renders the queries (ex: React, Java/Typescript, or Swift)   

For the purposes of this post, we’ll focus on the last two components: the server and frontend. On the server-side, you’re trying to achieve the following outcomes:

  1. Connect to your database source
  2. Define the GraphQL query structure
  3. Provide an endpoint for the frontend to send queries

One and three are pretty fixed. Reconnecting to a database means refreshing the server each time you want to make a change, and you typically don’t want to have your endpoint constantly changing the address and the port where it's reachable. But part two, defining our query structure, has some elements for adding dynamic values. 

How it works

Here’s an example of a GraphQL query object for a Node express server that’s retrieving data from a Postgres Database. In this example, I’m also using a tool called Join Monster, which helps translate GraphQL to SQL for interacting with Postgres: 

 const Hero = new graphql.GraphQLObjectType({
   name: 'Hero',
   extensions: {
     joinMonster: {
       sqlTable: ‘hero’,
       uniqueKey: 'id',
     }
   },
   fields: () => ({
     id: { type: graphql.GraphQLInt },
     name: { type: graphql.GraphQLString },
     appearsIn: { type: graphql.GraphQLString },
   })
 });
 const QueryRoot = new graphql.GraphQLObjectType({
   name: 'Query',
   fields: () => ({
     hero: {
       type: new graphql.GraphQLList(Hero),
       resolve: (parent, args, context, resolveInfo) => {
         return joinMonster.default(resolveInfo, {}, sql => {
           return postgres.query(sql)
         })
       }
     },

I’ll quickly summarize what’s happening here. This block of code creates a new GraphQL Object called Hero. Within that object, I want to have query fields that include id, name, and appearsIn. Lastly, the QueryRoot constant is what we use to define the schema. Put together using the "Star Wars" data from above, the query would look something like this:

{
  hero {
	name
	appearsIn
  }
}
{
  "data": {
	"hero": {
  	"name": "R2-D2",
  	"appearsIn": [
    	"NEWHOPE",
    	"EMPIRE",
    	"JEDI"
  	],
	}
  }
}

Look familiar? That looks a lot like the query at the beginning of this blog, right? It’s not the same code (spoiler alert) but the structure is similar minus one specific component which we’ll cover in a bit. Going back to our goal of changing from "Star Wars" to "Lord of the Rings," let’s assume we have a table in our Postgres database that is built with the same structure, but different data. If that were the case we could do something like this:

 const Hero = new graphql.GraphQLObjectType({
   name: 'Heroes',
   extensions: {
     joinMonster: {
       sqlTable: `{table}`,
       uniqueKey: 'id',
     }
   },
   fields: () => ({
     id: { type: graphql.GraphQLInt },
     name: { type: graphql.GraphQLString },
     appearsIn: { type: graphql.GraphQLString },
   })
 });
const QueryRoot = new graphql.GraphQLObjectType({
   name: 'Query',
   fields: () => ({
     hero: {
       type: new graphql.GraphQLList(Hero),
       resolve: (parent, args, context, resolveInfo) => {
         return joinMonster.default(resolveInfo, {}, sql => {
           return postgres.query(sql)
         })
       }
     }, 

Notice the difference? We’ve changed sqlTable: 'hero', to sqlTable: {table}. Why? This is where our flag comes into play. Using a string-value feature flag, we could pass a different table name to this field. In LaunchDarkly, it would look like this:

When disabled, this flag would provide the value star_wars, enabling it would provide lotr. In this example, we’re saying that these are the tables that exist in our database. Changing them would instruct the GraphQL which one it should use to source the data from. Using this method, we could iterate on different database tables without having to re-write our query parameters, assuming that the table structure is the same. But what if they aren’t the same? Or what if we plan to have relational data between multiple tables within a database? In that case, this method likely won’t work without manual intervention. To solve that issue, let’s migrate to the frontend! 

Controlling GraphQL queries in the frontend 

One of the really powerful capabilities of GraphQL is the ability to maintain and display relational data across multiple tables. Going back to our initial query example, there’s a friends field that returns an array of names. What’s happening is that somewhere in our GraphQL structure we’ve coded certain fields to retrieve information from another table using a common key value from our first table. Visually it looks something like this:

Parent Table (called hero):

“ID”

“Hero”

“Friends”

1

“R2-D2”

[Child_Table.name]

Child Table (called friends):

“ID”

“Name”

“Reference ID”

1

“Luke Skywalker”

1

2

"Han Solo"

1

3

"Leia Organa"

1

And using Join Monster again, the code would look like this:

 const Hero = new graphql.GraphQLObjectType({
   name: 'Hero',
   extensions: {
     joinMonster: {
       sqlTable: ‘hero’,
       uniqueKey: 'id',
     }
   },
   fields: () => ({
     id: { type: graphql.GraphQLInt },
     name: { type: graphql.GraphQLString },
     appearsIn: { type: graphql.GraphQLString },
     friend: {
	type: graphql.GraphQLList(Friend),
	extension: {
	joinMonster: {
sqlJoin: (friendsTable, heroTable, args) => `${friendsTable}.name = ${heroTable}.id`

   })
 });

Essentially this is just telling Join Monster to merge the two tables to create a list for friend based on the id of our parent table. 

With this logic, it’s a bit harder for us to do a simple table switch. Both tables need to have the exact same structure, column names, and the child table would need to have the same name. There are too many moving parts for us to use our flag from earlier, so what can we do? 

If you remember, we said there are three components in our GraphQL application: the database, the API, and the frontend. Well we can solve this challenge listed above in the frontend portion of the application! The way we do this is by again using string feature flags and trusty conditional statements. Here’s an example of what this could look like if we were were rendering the queries in a React application: 

if (table == “star_wars”) {
const { table } = useFlags();

const { loading, error, data } = useQuery(STAR_WARS_QUERY);
if (loading) return <p>loading</p>;
       	if (error) return <p> Error: {error.message}</p>;

       	return ({data.star_wars.map(({ id, name, friends }) => (
		<p key={star_wars.id}>{star_wars.name} is friends with {star_wars.friends}</p>
	))}
	);
	}
else {
const { table } = useFlags();

const { loading, error, data } = useQuery(LOTR_QUERY);
if (loading) return <p>loading</p>;
       	if (error) return <p> Error: {error.message}</p>;

       	return ({data.lotr.map(({ id, name, friends }) => (
		<p key={lotr.id}>{lotr.name} is friends with {lotr.friends}</p>
	))}
	);
	}

What we’re doing here is telling the React frontend to display, {the hero} is friends with {a list of friends}, but the query and data that it should use is based on the value of the table flag. If the flag returns the value of star_wars, we should return the values from the "Star Wars" table, if it’s anything else, return the "Lord of the Rings" table.

Now, a couple important things to note. This code is assuming that you are using the Apollo React Client. Apollo is a state management tool for fetching data from a GraphQL API. This is where we get the useQuery function and, as we’ll cover in a second, how we tell React what API URI to use. Another thing you might have noticed are the STAR_WARS_QUERY and LOTR_QUERY values. These are variables for  GraphQL queries that we have defined elsewhere in the code. Using this method, it assumes that you have done the following: 

  • You’ve configured your GraphQL server to support these query structures
  • You have the data sources connected to that GraphQL server
  • You have defined the GraphQL queries somewhere in your frontend code 

Another question you might have, is why use a string value instead of just a boolean? 

Well, sometimes, we might want to try multiple variations for different users. Maybe we’re trying different query structures within a specific user segment and we want to serve up a couple of different variations to those users. Using our example above we could have a variation for star_wars, lotr and star_trek, all with different query structures.

So now we’ve shown that we can switch between tables that share the same data structure on the server side, render different queries and tables through our frontend, the last step is to see how we can switch between different data sources with feature flags. 

Switching data sources with feature flags

At some point, we may choose to migrate databases entirely. Maybe we’re switching technologies (Postgres to MongoDB) or maybe we’re concerned about the growing number of tables in our existing database. In these circumstances, we may not be changing the query structure or frontend design. We just want to retrieve information from Source B instead of Source A. Once again, feature flags come to the rescue, but this time in conjunction with the Apollo Client that we mentioned earlier. 

Apollo is an extremely popular tool for GraphQL users. It offers server, router, and client capabilities all designed for applications with GraphQL at the heart of it. For the purposes of this post, we’re going to focus on the React Client, but I highly recommend you check Apollo's documentation if you’re curious about its other capabilities. As mentioned above, the Apollo Client allows us to define the upstream URI for our API connections within our React application. Typically the code looks something like this: 

const client = new ApolloClient({
   uri: `http://localhost:5000/`,
   cache: new InMemoryCache()
 });

This URI can be a dedicated address or just a path, like when interacting with other microservices inside a Kubernetes cluster and we only need the service name and port to establish a connection with another pod. I’m sure you’ve already guessed where this is heading, but this URI field can also be a dynamic value passed in by a feature flag. In that case we would simply change the code to something like this: 

const client = new ApolloClient({
   uri: `${apiAddress}`,
   cache: new InMemoryCache()
 });

But why? This is a fairly small change, why would we need to add a feature flag here? Well let’s use a Kubernetes cluster as an example. One of the really powerful things about Kubernetes is that we have the ability to very quickly deploy or destroy new pods. Since pods can exist independently of one another, there is little harm in spinning up a new one to do testing. 

Let’s say we’d like to migrate from using Postgres to MongoDB. As most operators would tell you, data migrations can be tricky and impact a lot of end users, both positively and negatively. Using feature flags, we could do a progressive roll out using a new API for our new database deployment. Within that flag, we could set a percentage rollout that says 10% of user traffic goes to our new database and 90% goes to our existing one. Once we feel good about the performance, we can either switch it all over to the new database or gradually increase the roll out and see what happens. No editing our GraphQL query structures, no changing tables, just a (hopefully) seamless transition for our customers that goes undetected. 

Conclusion

To summarize we have three ways that we can utilize feature flags with our GraphQL applications:

  1. Make server side changes, like dynamically changing tables
  2. Render frontend variations by targeting different tables 
  3. Change data sources by updating our upstream URIs

These capabilities are not unique to GraphQL, but instead they just show you another way to think about how feature flags fit into the grand scheme of your applications, not just frontend changes. 

The logic we applied here works with other APIs, proxy solutions, and data sources. The key is to try to locate areas in your code that dynamic values might be beneficial for assessing different variations of strings, numbers, or JSON objects.    

Hopefully this has sparked some ideas for where you see feature flags fitting into your code! Happy flagging! 

Related Content

More about Best Practices

November 17, 2022