In the GraphQL specification, we have different built-in scalar types. We have described them in the dedicated article. 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. The implementation of the GraphQL custom scalar can vary based on the language you use for building GraphQL server. In some of these implementations, it may not be even possible to use custom scalars. In our case, we will on building custom scalar with our official graphql-js library.
You can quickly start with:
install dependencies with
and start the server in development with
Custom scalar definition
As mentioned the built-in scalars might not be able to fulfil all the required parsing and validating of the input or output. That is the reason why we need to sometimes implement our own scalar. In this quick tutorial, we will implement DateTime custom scalar, which can be used for all date fields in your project. You can replicate all the steps also to build your scalars based on your needs. 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 already predefined 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:
- serialize: result coercion
- parseValue: input coercion for variables
- parseLiteral: input coercion for inline arguments
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 and parseLiteral
The parseValue and parseLiteral function refer 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 for our Task type. 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 GraphQL Playground 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 task-db.ts file and reassign createdAt or updatedAt to a string which is not in ISO8601 date format. We 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.
- Date Time: It is a library you can use for more precisely defined Date Time scalar. In this library, Time and Date scalars are also available.
- Custom scalar collection: This package is a collection of custom scalars .