GraphQL is one of the most fantastic tools presented in the software world in the last few years. That’s for many reasons: its strongly typed schema, avoiding over-etching or under-fetching, a handy tool for both server and client-side, composing multi API (Stitching), and the great community.
Directives are among the most powerful features of GraphQL that enable you to enhance and extend your API.
The directive is a function that decorates a portion of the GraphQL schema to extend its functionality. For example
@UpperCase() in the following example:
@UpperCase directive, as its name implies, would uppercase the user name and then return it.
There are two types of directives:
- Schema Directives.
- Query Directives.
Let’s know the differences between them from the built-in directives.
Till now, there are four approved built-in directives:
@deprecated(reason: String)which marks a portion of the schema as deprecated for an optional reason (Schema Directive).
@specifiedBy(url: String!)which provides a scalar specification URL for specifying the behavior of custom scalar types (Schema Directive).
@skip(if: Boolean!)if it is true, the GraphQL server would ignore the field and wouldn’t resolve it (Query Directive).
@include(if: Boolean!)if it is false, the GraphQL server would ignore the field and wouldn’t resolve it (Query Directive).
From the previous examples, you may notice that:
- The Schema Directives are defined in the schema itself and run while building it, and they are used by the schema designer.
- The Query Directives are used in the query and run while resolving it, and they are used by the end-user.
From the previous examples, you may ask why should we use directives while we can perform the uppercasing logic in the resolver itself depending on a field argument? This question will lead us to clarify the pros and cons of each other.
Unfortunately, different GraphQL servers, clients, and tools deal with GraphQL directives differently and support them to different extents which makes conflict among them.
For example, Relay doesn’t set into account using the Query Directive when querying the same field from the cache.
Take a look at the following example. This query runs for the first time and then cached:
Run the same query for the second time after caching but with the
The second query should return ‘HELLO WORLD’ however, Relay returns the same response as the first query which is existed in the cache even though we use the
@UpperCase directive which is completely ignored.
From the previous example, you note that depending on Query Directives is inconsistent due to the different handling from the GraphQL providers, as a result, GraphQL Tools discourages using the Query Directives:
In general, however, schema authors should consider using field arguments wherever possible instead of query directives.
On the other hand, using directives has some advantages:
- Your code will be cleaner by improving its reusability, readability, and modularity which respects the DRY Principle.
- Respecting the Single Responsibility Principle.
- If you want your clients to extend your GraphQL API with new functionalities without touching your code, you can depend on directives to fulfill this task.
So to summarize this point, to be on the safe side, as GraphQL Tools advises, you should use the field arguments instead of Query Directives. So the previous example should be:
Or you can use the Query Directives only if you know what are you doing!
Basically, there are many possibilities for using custom directives. Let’s create some of them as practical examples and apply them to the same query
You can find the complete code 👉 here.
First of all, we will define this schema:
- Let’s implement the upper-case directive and let’s call it
Firstly, we need to define the directive location.
Secondly, we need to define the directive transformer function that is responsible to apply the directive logic on every field having this directive.
Thirdly, we need to transform the schema by applying the directive logic.
Fourthly, we can apply it to the
title field as follows:
That’s it, now you can use the
@upper directive at any string field.
- Let’s implement a directive that loads this post from a third-party API, and let’s call it
Let’s move on with the same steps:
Now we can apply it to the
post query as follows:
- Let’s implement another directive to optionally format the date of the post
createdAtfield and call it
@date(format: String = "mm/dd/yyyy"):
Now, we can apply this directive to the
createdAt field as follows:
- Let’s implement a directive to authorize access to this post, let’s call it
That’s it, now the directive is ready to apply:
Try to set
role to be
OPERATOR instead to validate the directive effect.
- If we want to auto-generate a UUID for the post, we can create the
You may notice that we used
OBJECT instead of
FIELD_DEFINITION because this directive will be applied to GraphQL Types like
Then we can apply it to the
Post type as follows:
- Also, we can implement a directive to validate a string length like the post’s
bodyfield. Let’s call it
@length(min: Int, max: Int):
Now, apply it
Try to set
min: 1000 and check the validation.
Simply put, Directives are a very great tool that can be used to enhance your GraphQL API. In this article, we implemented some use cases of directives only to show you the power of directives, and consequentially, you can implement your own ones that fit your own situation.
- GraphQL Spec
- Schema directives
- GraphQL directives are underrated
- Field arguments vs. directives in GraphQL
If you liked this article please rate and share it to spread the word, really, that encourages me a lot to create more content like this.
If you found this article useful, check out these articles as well:
- Liskov Substitution Principle Isn’t Complex. Just Give It A Try
- Strategy vs State vs Template Design Patterns
- MongoDB GridFS, Made Simple
Thanks a lot for staying with me up till this point. I hope you enjoy reading this article.