Browsing the GraphQL specification, you might stumble into the section on variables and wonder, “Why on earth GraphQL does have variables?”
The spec quietly mentions the rationale, and if you read too fast you might miss it:
A GraphQL query can be parameterized with variables, maximizing query reuse, and avoiding costly string building in clients at runtime.
I think GraphQL variables are pretty neat, and deserve a bit more exploration and context.
Here’s a simple GraphQL query with a variable, using GraphQLHub’s Reddit Schema:
In plain-English, this query says it accepts one variable (username), and it is a required string. Internally, the query uses that variable to determine the data we’re requesting.
There are nifty things going on here that might not be immediately obvious:
- All of the variables needed for this query are declared up-front, so your tooling can quickly throw an error if you forget to pass a value
- Variables are typed, and your tooling can enforce that the passed types match those needed in different parts of the query
- All variables must be used in the query — if a variable is declared but not used, GraphQL regards the query as invalid
How do we set the values for variables? The GraphQL spec doesn’t impose any constraints, similar to how it does not specify the mechanics of GraphQL requests in general. The GraphQL-JS ecosystem generally uses a separate JSON blob:
This JSON string is passed as an additional field when POSTing data to the server. To be more explicit, our request ends up looking like this:
Variables can also have default values (runnable here), so you may not need to pass any variables on your own:
After finding all of this out, I wondered to myself, “But why does GraphQL have these features?” So I did some research and think I have a better understanding.
The Road to Variables
This means that you should not manipulate your queries at runtime. In other words, logic that directly mutates query strings (or query syntax trees) like this is an anti-pattern:
There’s nothing in the GraphQL spec stopping you from doing this, but it has some major downsides as your product and team grow.
For example, one of GraphQL’s distinguishing features is validation. The GraphQL server lets clients know what data it has and what types to expect, which is a strict improvement over the loose contractual nature of REST.
But if you manipulate queries at runtime, you can’t take advantage of tooling that could quickly detect compatibility issues between client and server. If your queries are static (declared in files or otherwise), your server unit tests could validate all of your iOS app’s queries before merging breaking changes.
Imperative GraphQL query construction at runtime can also lead to bugs. Imagine writing the hefty parts of Facebook (Newsfeed, Timeline, etc) by concatenating and injecting strings. There are so many subtrees of data (posts, their comments, their likes, etc) that the logic would be rather complex and tough to debug. Just as the declarative nature of React makes your view code easier to reason about, declaring the “universe” of queries up-front makes your client-server interactions simpler to maintain.
Finally, manipulating queries at runtime might run into performance problems. Creating lots of ephemeral strings on some environments leads to garbage collection penalties, which may cause dropped frames or other user experience hiccups. Parsing those strings into some kind of a syntax tree, a technique used in local caches like Relay, might also be an expensive operation in some cases.
For these reasons, we want to write (and/or parse) our GraphQL query strings ahead-of-time. And if we can’t perform runtime logic on our queries, Turing-esque features like variables, directives, and fragments naturally emerge. For many products, these primitives are generally expressive enough for all the operations we’d want to perform.
So that’s why variables exist — to help us compose our GraphQL queries ahead-of-time as often as possible. I hope this gives some more insight into the thoughtfulness behind GraphQL and how it’s been designed to address real engineering needs (at scale! as they say).