How to design GraphQL queries and mutations: custom scalar
In the first part of this series we discussed the different built-in scalars available in the GraphQL specification. In the second part we moved to enums. In the last part of this series we will return to scalars. However, we will concentrate on the so-called “custom scalars”. We will first define the model scalar for DateTime, then we will extend the task definition for the fields createdAt and updatedAt, which will basically be time stamps for each new task.
As we discussed in this article, in GraphQL specification we have different built-in scalar types. But what can we do if we need to use scalars which are not defined in the GraphQL specification? The answer is to use the so-called “custom scalar”. As in the previous articles we will use graphql-js. The implementation of the GraphQL custom scalar can vary across other GraphQL implementations. In some of these implementations, it may not be even possible to use custom scalars.
We will continue with the same repository as with the enums and built-in scalars. You can clone the GitHub repository using this command
git clone https://github.com/a7v8x/express-graphql-demo.git -b feature/3-graphql-scalars
Custom scalar definition
Now let’s go right into the designing of the DateTime scalar. We will use validator js library to test if the value is in ISO8601 date time string format. The simplified definition of the DateTime scalar can be as follows
In graphql-js, each custom scalar type definition has different fields and methods that should be defined. Just as with input/output object types, we have to define the required field name of the scalar. The description field is once again mandatory. If you read the article on built-in scalars we went through input and result coercion for each type. This is defined in graphql-js library for built-in scalars. However, when we define our own custom scalar, we have to specify these rules. That is why we have to define a couple of methods for each custom scalar. These are:
The serialize function refers to the result coercion. The first argument for this function is the received value itself. In our case, we would like to check if the value is in ISO8601 date format. If the received value passes the validation, we want to return this value; otherwise, we would raise the GraphQL error.
- parseValue, parseLiteral
The parseValue and parseLiteral function refers to the input coercion. The difference is that in parseValue, we refer to the input passed using the variables. When we use parseValue, the first argument of this function is the value itself. On the other hand, the parseLiteral function has its first argument the ast value in the following format
That is why we have to extract the value from the ast variable and again validate it by our rules.
We have completed the definition of our custom scalar, and therefore it is possible to implement it in the schema. We will apply it on the timestamp fields createdAt and updatedAt. We can simply import our Date Time scalar and use it as a type for these fields.
Now let’s check out if our rules, which we defined works as expected. Just go to /graphiql and try to call the basic getTasks query:
If our custom Date Time scalar is implemented correctly, you should get something like this:
Now let’s test out our serialize function. Just go to the taskDb.js file and reassign createdAt or updatedAt to a string which is not in ISO8601 date format. I will change the createdAt field to 2017–10–06T14:54:54+0. Now if we try to call the getTasks Query, the GraphQL server will raise the following error:
Custom scalars provide an even greater benefit when we have a collection of our predefined custom scalars. Then we can centralize most of our custom validations and move them from our resolver functions to our custom scalar types. Our simple Date Time scalar was a great model example. However, if you want to use more complex scalars like JSON or more precisely defined Date Time scalars, etc., it is possible to use some of the open sourced npm packages. These include:
- JSON: Using JSON as a scalar https://github.com/taion/graphql-type-json.
- Date Time: It is a library you can use for more precisely defined Date Time scalar. In this library, seperate Time and Date scalars are also available https://github.com/excitement-engineer/graphql-iso-date.
- Date Time and email: This package is a small collection of custom scalars https://github.com/adriano-di-giovanni/graphql-scalars.
Even that there are some of custom scalars published as an open source. There are just a few of them. If you have a good tips on some custom scalar definitions, feel free to share it in the comment section below!