I’ve been using GraphQL in some form or another for a couple of years now, and I have… opinions about it. Opinions notwithstanding, it’s a supremely flexible and powerful tool, and I just want to highlight something here that it seems quite a few people don’t know about.
That something is Directives, which is a way of optionally including fields. But why is this useful?
Scene of the crime
Let’s imagine that you’re working on a new feature on a well established app. This app, for a contrived example, shows a list of types of bird. It’s a scrollable list of carousels, with each item showing a name of a bird under a header. For whatever reason, the GraphQL you use to fetch this information looks like this:
query GetBirdTypes() {
falcons {
name
image
prey
}
crows {
name
image
lifespan
}
# etc
}
The app is doing well, making tonnes of money. Who doesn’t love birds. Product decides that you need to support hummingbirds as a new category; it’s migration season for the Ruby Throated Hummingbird, and you need to display when the migration is likely to be in your area.
Easy, you say to yourself. Backend have provided a new type, hummingbirds
, and you dutifully add it to your query:
query GetBirdTypes() {
# ...
hummingbirds {
name
image
migrationTime
}
“Job done”, you say to yourself, turning your attention to the nectar feeder in the yard. It is migration time after all.
Unfortunately, there’s a problem. As a consumer of GraphQL, you have no idea what’s happening under the hood, and it turns out that the GraphQL server has to make a request to a third-party server specifically about hummingbirds when this new field is added. This server is having a bad day and timing out, which causes the entire query to fail. Users can’t see any birds. This is bad.
Because you’re a responsible engineer, you feature-flagged all the new code, and you disable it. However, the problem doesn’t actually lie in that code - rather with the backend itself, and turning off the feature does nothing, because ultimately you’ve modified a query that is used elsewhere. The home feed team is angry at you, and you’re sad.
This is the insurance that directives offer - you can optionally choose to query new fields, and therefore cut out new, risky paths in your GraphQL queries and guard against a new type of failure which is otherwise largely out of your hands.
Enter Directives
Your day would have been saved if you had done something like this:
query GetBirdTypes(
$includeHummingbirds: Boolean = false
) {
# ...
hummingbirds @include(if: $includeHummingbirds) {
name
image
migrationTime
}
}
And all you have to do is pass your feature flag to the query, presumably you’d be using Apollo:
suspend fun getBirdTypesList(): Result<BirdTypes> {
val response = apolloClient
.query(
GetBirdTypesQuery(
includeHummingbirds = isMyFeatureFlagEnabled
)
)
.execute()
// ...
}
If you want the inverse of this behaviour, you can instead use @skip(if: Boolean)
.
Also note that GraphQL supports default values! So you can reduce the blast radius further, and teams re-using this query who don’t care about hummingbirds (people who are wrong) don’t have to do anything at all.
You can also use these in Fragments (not those Fragments
):
fragment Hummingbirds {
name
image
migrationTime
call @include(if: $includeBirdCall) {
url
length
}
}
}
Just make sure that the query (or mutation) utilising this Fragment also takes a parameter includeBirdCall
.
Closing Thoughts
This technique has saved many a bacon, and it can save yours too. If you use GraphQL at any scale, consider using Directives next time you add a field to an existing query. It’s super easy to do and a great example of the flexibility of GraphQL. Thanks for reading.