Recently, GraphQL has become a trending solution for the server side implementation. Most of the companies that I know have decided to use GraphQL because of the responsibility. The ecosystem of GraphQL is growing at a fast pace, but people still don’t know all its powerful features. And one of those features is called directives.
GraphQL directives have been created to deal with the problem of recurring tasks during the implementation. It’s kind of syntax sugar for your GraphQL server implementation.
Those common recurring tasks could be:
- Creating a custom field
- Getting data from the REST API
- Permissions management
Computing custom properties
One of the common situations is when you need to create a new field, that is derived from previously defined fields. Let’s see how we can resolve that issue by taking the native approach.
Our front-end team tells us that they need to have fullName returned from the server (yes, I know that is a trivial example). Let’s start with updating our schema and add fullName.
The second thing we need to do is to update your resolvers. We need to create User resolver with fullName that will return the data that will fit our requirements:
As you can see it’s not so hard to implement, but imagine when you have more tasks like this, also more complicated. Be smart and use a directive that will save some of your precious time ⏰
To simplify that common task, I wrote a small directive called graphql-directive-computed-property. It allows you to create a computed property from every other property that lives in a particular type without touching your resolvers.
Here is an example of the usage:
And that’s all that you need to do (apart from installation, of course)!
As you can see we’ve added @computed directive definition to the new field and specified their content. Inside value argument, you have access to all fields on that type.
Another frequent use case is to manage integration with existing REST APIs. Let’s imagine we have the below schema on our server:
In our case, we want to get all the data from various REST API. We start writing resolvers one by one and after some time we get something like this:
Sure, some of you may think that we can create an abstraction over the fetch library, which could remove unnecessary duplication. But there’s a better approach — we can use the directive:
It is a directive that helps you write the server which needs to integrate with the REST API. See below how the implementation looks with the directive:
Finally, this is our new schema definition. As you can see it looks almost like the previous one, the difference is that we have the extra @rest annotation on almost every field.
Let’s explain the above code. @rest annotation accepts two arguments. First of them is URL and takes the string with a path to the response from where you would like to get the data. The extra thing here to notice is that we have an access to query parameters inside the URL parameter. Our path can base on the query arguments. The second argument accepts a path to the data from the response.
Now is time to write the resolvers:
We don’t need any resolvers 🎉 🎉 🎉 Why? All work is getting done inside the directives.
A quick explanation of what we do here:
me query will get the data from https://randomuser.me/api/?gender=$genderwhere $gender parameters we get from the query parameters. The extractFromResponse parameter tells us that we want to get the first element from the response array.
users query return all the data from https://api.github.com/users in that case we get all user and return them from our server.
Type Me contains the admin field where we want to get data from https://yesno.wtf/api, and extract from the response object answerproperty.
As you can see, we can quickly enhance our server with real data from REST API with no big effort.
I described two directives (permissions-manages is coming soon, stay tuned!) but you can check others directives at graphql-community There are other directives there that can be helpful.
How to create your own directives
I have created a template repository with all the necessary things to ease the start. All you need to do is clone the repository:
After that, you are ready to start. Now I’m going to tell you about a few important things related to that repository:
- your directive implementation should start in index.js on the root folder,
- the example server is placed onexample/index.js. For server implementation we used apollo-server-express,
- under example/schema.js you can find the schema definition — a place to use your directive and create the custom schema,
- the __tests__ folder contains all the tests related to directives (we use Jest for testing).,
- if you would like to start the example server, you can use yarn dev scripts - this also runs GraphiQL where you can test your queries; your server will be reloading itself after each change.
Another great source to learn about the implementation details is the documentation of graphql-tools, which can be found here.
If you come up with any interesting ideas after finishing your read, let me know and we’ll publish it on graphql-community 💪💪💪
At the end, I would like to encourage everyone to join graphql-community and support creating the ecosystem for GraphQL. Also, reach out if you have already created any directives or have any other interesting ideas :)
I would like to say big thanks to Evans Hauser and his introduction into Directives 👏 👏. Also, I would like thanks to all Callstack team member of Open Source Evangelists. Check more details about that group here. I am convinced that without all the effort this community wouldn’t have never been born. 👋