When designing GraphQL schema we have to define the so-called primitive values of each query. In GraphQL the primitive values are represented by scalars and enums. The GraphQL specification has different built-in scalars. In this first part of this series, we will concentrate only on the built-in scalars. To demonstrate real applications of the built-in scalars we will apply them in a modelling query for retrieving tasks from the existing “in-memory” database and also for adding new tasks to a database. All examples are implemented in the repository.
You can quickly start with:
install dependencies with
and start the server in development with
You should be now able to access GraphQL Playground.
In GraphQL we deal with 8 different types:
In the article Input object type as an argument for GraphQL mutations and queries we were focusing on objects and input objects. In this series, we are moving to scalars and enums. In this first part, we deal only with built-in scalars; enums and custom scalars are covered in the further articles.
When we query or mutate data using our GraphQL schema, we first have to understand two operations that occur in GraphQL servers. These are:
- result coercion: upholding the contract of a type which we receive from the server (basically upholding the primitive values or object type)
- input coercion: upholding the contract of a type for input
arguments that we pass into the GraphQL query or mutation To fully understand the algorithm behind the coercion we have to go into the GraphQL specification. For us, the simple explanation as above is enough to get started. It is important to recognize that the rules for each scalar type are different, therefore we have to discuss these rules for each type and corresponding result and input coercion.
Scalars are primitive values in GraphQL. This means that if we imagine each GraphQL response as a hierarchical tree graph, we can call the primitive values leaves in terms of graph theory. To imagine, check out the visualized hierarchy tree graph of our response for our query getTasks and mutation createTask, which we will code in this short tutorial series
In this first part we will deal only with id, name, completed, progress and taskPriority as these are represented by built-in types. In the second part we then extend the Task type with the fields updatedAt, createdAt, and state.
In this list, we will go through each default scalar type. We will also discuss rules for result and input coercion and where this scalar should be used.
The ID scalar type represents a unique identifier, often used to re-fetch an object or as a key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID.
If you use, for example, some caching client like Apollo, each type should have at least one ID. This allows us to perform a normalization of queries, making it possible for us to update things in Apollo internal redux store automatically based on the unique id (edit: Apollo 2.0+ does not use redux as its store any more). This is also performed in Relay by the global identifier at each node. ID is often numeric or in another format such as base64 encoded value. It is always serialized as a string because we want to achieve uniformity across different formats.
- 5 is parsed into a string as "5"
- However, if we pass true, it is not parsed as "true". GraphQL will raise the following error:
the same case is even for the Float type and etc.
ID is serialized into a string if possible:
- 6 is serialized into “6”
- true is serialized into “true”
- 3.00 to “3”
The Int scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2³¹) and 2^31 - 1.
- string “10” will raise an error when passing it as a Int argument. Also 10.00 will raise an error. Only pure Int values are accepted.
- The string “3” is serialized into 3 and Float 3.00 is also serialized to 3.
- The string “3” is serialized into 3 and Float 3.00 is also serialized to 3.
The Float scalar type represents signed double-precision fractional values as specified by IEEE 754.
- String “3” or “3.00” will raise the error in the same way as with Int. When we pass 3 as an integer it is parsed into 3.00.
- 3 is serialized into 3.00, “3.00” is serialized into 3.00, etc.
The String scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
The rules are similar for ID. The input 5 will raise an error etc.
- 1 is serialized into "1"
- 1.00 is serialized into "1", true is serialized into "true" and so on.
The Boolean scalar type represents true or false.
When we pass "true" as an argument, it raises an error.
If possible, all non-boolean values are coerced into boolean values. The example can be 0 and 0.00 coerced into false as well as 1 and 1.00 serialized into true. However, it is important to emphasize that string "true" or "True" is not coerced into true. GraphQL server will always return true value for every string with the length greater than 0.
An application of built-in scalars
Now let’s go right into implementation. You can follow the code snippets in this article or clone the git repository
If you are new to GraphQL, it may also be helpful to check out previous articles in GraphQL mastery publication, especially the one on creating mutations as we use them as prerequisites for this article in terms of understanding GraphQL. We will also use parts of the code that we built in previous articles. However, everything can be obtained from the mentioned repository. In the GitHub project we do not use a real database. The in-memory database is enough to get started.
To demonstrate each type of built-in scalar, let’s design a Task object type in the following way.
We will describe DateTime and TaskStateEnum types in our articles In our code we use graphql-js library. Then we can describe Task type as follows.
Now let’s create a simple array where we will store our seeded tasks. Then we can make the simple getTasks query and the createTask mutation, which we can call from our resolver function in GraphQL schema. The getTasks query is used to query all the tasks from the in-memory database. The createTask mutation is then used for creating a new task.
The corresponding GraphQL schema and resolvers for retrieving tasks from memory DB is as follows:
Now we have everything we need to perform query for retrieving tasks against the schema. Just go to /graphql and paste the following query
If you use the Github project code you should get the following result:
First, let’s create an input object type for creating a task. We will use some defaultValues for some of the fields
Now we can move on to designing the createTask mutation
We can execute this mutation and add a new task into the “in-memory” database. We can also use variables as we discussed in another article or just pass it as an inline argument.
The result should be something like this
To illustrate the full example, GraphQL schema in SDL is as follows:
Input & result coercion of demo examples
For the sake of demonstration, we can now test some rules for result or input coercion. For example, let’s take a result coercion for ID and test the rule that an integer is serialized into a string.
6 is serialized into “6”
We can change the value of id to 6 in task-db in order to test such task. We can confirm the result coercion rule by calling the getTasks query in GraphQL playground in the same way as above:
In a similar way we can confirm some rule for input coercion. Let’s take for example rule for Int.
string “10” will raise the error, when passing it as an Int argument
We can show this rule on the createTask mutation,
which raises the following error
All built-in scalars can be used as fields (primitive values) for input types as well as for output types. Each built-in GraphQL scalar has different rules for input and result coercion. We can also use enum and custom scalars as we will find out in the next part of this series.