The leaf nodes of the graphql type system are called scalars. Once you reach a scalar type you cannot descend down any further into the type hierarchy. A scalar type is meant to represent an indivisible value.
The graphql specification says that all implementations must have the following scalar types.
GraphQLString- A UTF‐8 character sequence.
GraphQLBoolean- true or false.
GraphQLInt- A signed 32‐bit integer.
GraphQLFloat- A signed double-precision floating-point value.
GraphQLID- A unique identifier which is serialized in the same way as a String; however, defining it as an ID signifies that it is not intended to be human‐readable.
graphql-java adds the following scalar types which are useful in Java based systems
GraphQLLong- a java.lang.Long based scalar
GraphQLShort- a java.lang.Short based scalar
GraphQLByte- a java.lang.Byte based scalar
GraphQLBigDecimal- a java.math.BigDecimal based scalar
GraphQLBigInteger- a java.math.BigInteger based scalar
graphql.Scalars contains singleton instances of the provided scalar types
You can write your own custom scalar implementations. In doing so you take on the responsibility for coercing values at runtime, which we will explain in a moment.
Imagine we decide we need to have an email scalar type. It will take email addresses as input and output.
We would create a singleton
graphql.schema.GraphQLScalarType instance for this like so.
The real work in any custom scalar implementation is the
graphql.schema.Coercing implementation. This is responsible for 3 functions
parseValue- takes a variable input object and converts into the Java runtime representation
parseLiteral- takes an AST literal
graphql.language.Valueas input and converts into the Java runtime representation
serialize- takes a Java object and converts it into the output shape for that scalar
So your custom scalar code has to handle 2 forms of input (parseValue / parseLiteral) and 1 form of output (serialize).
Imagine this query, which uses variables, AST literals and outputs our scalar type
Our custom Email scalar will
parseValueto convert the
$mainContactvariable value into a runtime object
parseLiteralto convert the AST
graphql.language.StringValue“firstname.lastname@example.org” into a runtime object
serialiseto turn the runtime representation of mainContactEmail into a form ready for output
The methods can validate that the received input makes sense. For example our email scalar will try to validate that the input and output are indeed email addresses.
The JavaDoc method contract of
graphql.schema.Coercing says the following
serialise MUST ONLY allow
graphql.schema.CoercingSerializeException to be thrown from it. This indicates that the
value cannot be serialised into an appropriate form. You must not allow other runtime exceptions to escape this method to get
the normal graphql behaviour for validation. You MUST return a non null value
parseValue MUST ONLY allow
graphql.schema.CoercingParseValueException to be thrown from it. This indicates that the
value cannot be parsed as input into an appropriate form. You must not allow other runtime exceptions to escape this method to get
the normal graphql behaviour for validation. You MUST return a non null value.
parseLiteral MUST ONLY allow
graphql.schema.CoercingParseLiteralException to be thrown from it. This indicates that the
AST value cannot be parsed as input into an appropriate form. You must not allow any runtime exceptions to escape this method to get
the normal graphql behaviour for validation.
Some people try to rely on runtime exceptions for validation and hope that they come out as graphql errors. This is not the case. You
MUST follow the
Coercing method contracts to allow the graphql-java engine to work according to the graphql specification on scalar types.
The following is a really rough implementation of our imagined
such a scalar.