Scalars (ScalarTypeDefinition) are indivisible atomic data types in GraphQL that act as leaf nodes in the service. For the client, a valid Select Set must reach a leaf node, that is, a scalar node.

The GraphQL specification provides five types of scalars:

  1. Int: a 32-bit signed integer. The engine will throw an exception if the precision exceeds the range
  2. Float: a signed, double-precision floating point number that is thrown by the engine outside the precision range
  3. String: Indicates a character stringUTF-8Character sequence
  4. Boolean: the Boolean value
  5. ID: unique identifier of the resource

1. ID properties

The five types mentioned above are similar to their counterparts in other languages, and I believe readers are already familiar with them. The only type worth discussing is the ID type.

  1. On the performanceIDA type is just a value in string format. The engine supports string parsing as well as convertingIntParsed values are converted to string types;
  2. Semantically. “”ID“Type should be used to uniquely identify a resource object. That is, no matter how many times you query with the same ID, the result should be the same object. This is useful for caching.
  3. The engine does not limit the uniqueness of resolved values, and it is legal for query results to contain multiple nodes with the same ID value.

Let’s look at some examples to impress:

[
 // A string of characters
 {id: '1'},
 // int, which the engine will convert to a string
 {id: 1},
 / / float type
 // Invalid value, the engine does not support float conversion
 // A 'TypeError' error will be thrown
 {id: 1.2},
 // Repeat the first step above
 // The engine does not enforce the uniqueness of the 'ID' value
 {id: '1'}]Copy the code

2. Customize scalar types

In addition to the scalars defined by the specification, scalars within a business category can be defined on demand. The syntax is very simple:

scalar Datetime
Copy the code

Note that this is only a semantic category definition, and you also need to define serialization and deserialization functions:

new GraphQLScalarType({
  name: "Datetime".description: "Date-time scalar type".// Serialize the function
  serialize(value) {
    return value.toString();
  },
  // Parse the function
  parseValue(value) {
    if (typeof value === "string") {
      return new Date(value);
    }
    throw new Error("Parameter type error");
  },
  // Parse the function
  parseLiteral(ast) {
    if (ast.kind === Kind.STRING) {
      return new Date(ast.value);
    }
    throw new Error("Parameter type error"); }});Copy the code

Let’s look at each configuration one by one:

  1. name: Field name, please keep withschemaThe scalar type names specified in
  2. description: type description, useful in some diagnostic tools
  3. serialize: serialization function that converts the result to a numeric type suitable for HTTP transport
  4. parseValue: parsing function, used to pass the clientvariablesThe value passed as a parameter is of type Date
  5. parseLiteral: is also a parsing function that passes the clientLiteral parameterResolves to Date type

The parseValue and parseLiteral functions in the configuration are similar in function. Both parseValue and parseLiteral are used to parse client parameters.

Query (before: Datetime){users(before: $before) {id name}} variables {before: Query {users(before: "1991-02-19") {id name}}Copy the code

A few final caveats:

  1. If the type is determined it will not actInputTypeCan be omittedparseValue,parseLiteral.
  2. parseValueThe received isvariablesThe corresponding value in the object; whileparseLiteralThe receiver is the engine fromqueryThe AST node parsed from the statement. The AST node content looks like:
{// literal type "kind": "StringValue", // literal value" value": "1991-02-19", / / specify literal is [BlockStringValue] (https://facebook.github.io/graphql/June2018/#BlockStringValue ()) type "block" : False, // token position "loc": {"start": 18, "end": 30}}Copy the code

3. Return the scalar of the object

Scalar types also support the return of structured objects, as long as the engine has a serialize function that complies with the rules, anything is possible. We can write a scalar like this:

// Address object type, but this is a scalar
new GraphQLScalarType({
  name: "Address".description: "Scalar of object type",
  serialize(value) {
    // value is the object type
    // value = {city: 'shenzhen ', province:' guangdong ', country: 'China'}
    returnvalue; }});Copy the code

Note, however, that scalar types are indivisible and cannot be passed in a subset of queries:

{users {id name # Address bornOrigin}}Copy the code

Return result:

{
  "data": {
    "users": [{"id": "1"."name": "foo"."bornOrigin": {
          "city": "Shenzhen"."province": "Guangdong province"."country": "China"}}]}}Copy the code

The full code is here. While legitimate, using a scalar type to return an undetachable object violates the important principle of loading on demand and is not worth advocating unless a better solution is found. For example, when we need to deal with highly dynamic information structures and we want to transmit information in a structured and predictable way, we have to adopt this approach. Take the log as an example. For a relatively large-scale system, log formats are diverse. If you need to enumerate them one by one and convert them into GraphQL’S SDL one by one, the development and maintenance costs are very high.

conclusion

Scalars are atomic types in GraphQL that typically serve as leaf nodes for queries. The GraphQL specification provides five scalar types, of which ID is the most special, for uniquely identifying a resource instance. In addition to standard scalars, new scalars can also be defined on demand, as described above.