Life of a GraphQL Query — Validation
A few weeks ago I wrote about the first phase of what happens when a GraphQL query is executed: lexing and parsing.
We left off with an AST representation of a query which we obtained from the parser.
At this point we know the query is syntactically correct, but that doesn’t necessarily mean we can execute it.
Many things could be wrong with the query. The Author
type could very well not have a username
field, the argument id
could be of type Int
, etc.. The parser doesn't care about this. Instead, that is the job of the validation phase.
The GraphQL specification has an entire section that covers validation. Each validation rule is explained in detail accompanied by a pseudo implementation and examples of queries that pass/fail the rule. It’s a great resource that I’ve used countless amount of times when I wasn’t sure about a validation or when I wanted to confirm a behaviour in the GraphQL implementation that I was using.
After digging into various GraphQL implementations like GraphQL Ruby, Sangria, and GraphQL.js I noticed they all have one thing in common when it comes to validation: the visitor pattern.
While Wikipedia provides a theoretical explanation of the visitor pattern, I’d like to explore how it’s used in GraphQL implementations to power the validation phase. We’ll also look at some sample code from GraphQL.js.
First, let’s start with a simple query and its AST representation:
In order to determine whether the query can be executed or not, GraphQL.js’ validate
function will traverse the AST using a depth-first traversal. In other words, it will start at the root of the AST (in this case, the Document
node) and it will explore as far as possible along the first branch before backtracking and continuing on to the next branch.
Visitors (in this case, validations rules) can watch as the AST is being traversed and perform actions when a certain type of node is reached. In GraphQL.js, visitors can be implemented in different ways, but in their simplest form they are an object with a enter
and leave
function.
During the traversal, when a node is reached the enter
function of the visitor will get called. When backtracking, the visitor's leave
function gets called.
Here’s a simple visitor that logs whenever a node is entered or left. Notice in this example we are using GraphQL.js’ visit
function to traverse the AST. This is the same function that validate
uses.
Some GraphQL rules might only be interested by a specific type of node. For example, a rule might want to validate that an OperationDefinition
always has a name (i.e. query Name { .. }
.) For this reason each GraphQL.js rule can decide to hook onto individual types of nodes. This can be done by nesting the enter
and leave
functions under a key named the same as the kind of AST node of interest.
Here’s an example of a visitor that only cares about when an OperationDefinition
node is entered:
The OperationDefinition
key in the above example can be replaced with any valid AST node kind to observe that kind of node. A visitor can also observe multiple node kinds at once by setting the corresponding keys.
Now that we have a better understanding of the visitor pattern and how visitors are implemented in GraphQL.js, we can add the missing parts to turn this visitor into a GraphQL.js validation rule.
If you’re interested in learning more about the validation phase, check out GraphQL.js’ src/validation folder. A lot of what was discussed is explained as comments in the code.
While writing this article I started a project called GraphQL Schema Linter which is built on top of GraphQL.js’ validate
and visit
functions. I'm always looking for help so whether you are new to this or not come join the discussions in the repository.