The Power of GraphQL Directives
In short
This article discusses the growing popularity of GraphQL as a server-side solution and introduces the concept of GraphQL directives, which simplify common tasks like creating custom fields and integrating with REST APIs. The author also provides examples of using directives to enhance a GraphQL server and encourages community collaboration in developing new directives.
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 <rte-code>fullName<rte-code> returned from the server (yes, I know that is a trivial example). Let’s start with updating our schema and add <rte-code>fullName<rte-code>
The second thing we need to do is to update your resolvers. We need to create User resolver with <rte-code>fullName<rte-code> 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 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 <rte-code>@computed<rte-code> directive definition to the new field and specified their content. Inside value argument, you have access to all fields on that type.
REST API
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 for rest. It is a directive that helps you write the server which needs to integrate with the REST API. Here's 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. <rte-code>@rest<rte-code> 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=$gender where <rte-code>$gender<rte-code> parameters we get from the query parameters. The <rte-code>extractFromResponse<rte-code> parameter tells us that we want to get the first element from the response array.
- Users query return all the data from "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 <rte-code>answerproperty<rte-code>.
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 <rte-code>index.js<rte-code> on the root folder,
- The example server is placed <rte-code>onexample/index.js<rte-code>. For server implementation we used <rte-code>apollo-server-express<rte-code>,
- Under <rte-code>example/schema.js<rte-code> you can find the schema definition — a place to use your directive and create the custom schema,
- The <rte-code>__tests__ folder<rte-code> 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.
Summing up
Another great source to learn about the implementation details is the documentation of graphql-tools. If you come up with any interesting ideas after finishing your read, let me know and we’ll publish it on graphql-community.
I would like to thank Evans Hauser and his introduction into Directives. Also, I would like to thank all Callstack team members of Open Source Evangelists. I am convinced that without all the effort this community would have never been born.