🤔 Why GraphQL needs to support the union of scalars
In my latest article on LogRocket, Creating an @export GraphQL directive, I showed the screenshot of a GraphiQL client with red lines over the query argument definitions, which indicate there is an issue going on:
Why is this happening? For the demonstration for my article, I have needed to print the value of the dynamic variable in the response, as to visualize that it works well. For that, I created a field echoVar
, which simply echoes back the value contained in the variable.
Since the type of the values may not be known in advance (it could be a String
, Int
, any custom scalar, an object, or anything else), the type of echoVar
is a generic Mixed
type, to which all types can be "identified" with.
The consequence of using Mixed
is that there will be a mismatch when loading the query in the GraphiQL client, showing a red line in the argument definitions and an error when hovering over them:
I am not so troubled with this issue, because field echoVar
is not actually needed: it was just used to see that @export
behaves as expected. However, it is still annoying to see those red lines.
The solution currently supported by GraphQL is extremely verbose and unmaintainable: to create different echoVar
functions for each type (echoStringVar
, echoIntVar
, etc). This issue should be solved in an elegant way, avoiding the verbosity from having to declare a different field per type of response.
The GraphQL spec issue linked to in my article mentions that we could provide @export
with an additional argument type
, but this is potentially useful only for deducing the type of the object in the GraphQL server. The hack, though, deals with the type of the object in the query, on the client.
So, how to solve this issue? There are 3 possible approaches to it. All 3 are currently unsupported by GraphQL, but this situation could change in the foreseeable future.
1. Make all scalar types implement a Serializable
interface permalink
The trait in common among all scalar types (Int
, Float
, Boolean
, String
and ID
, and all custom scalar types) is that they are serializable. Hence, if they implemented a Serializable
interface, we could have field echoVar
return this interface, and it would be satisfied not matter which actual type it returns.
However, this doesn't work, because the spec says that an interface must include at least one field, but scalar types cannot resolve fields (that's only doable by the Object
type). Then, unless the spec is modified, scalar types cannot implement interfaces.
2. Support the Any
scalar type permalink
The Mixed
type I have use to represent any scalar type could be a type all by itself, a kind of wildcard type that says: I represent anything.
This use case is already being dealt with, through this pull request, proposing to add the Any
type. However, this pull request is 3 years-old, and doesn't seem to have much activity, so I don't hold my breath about it.
3. Allow fields to return unions of scalar types permalink
Even though currently only object types can be part of union types, there is a proposal to also support the union of scalar types.
With this solution, field echoVar
could be declared to return Int | Float | Boolean | String | ID
, and so all of these cases would be covered.
This is the solution that seems most promising of all 3. The issue has had recent activity, is directly related to the GraphQL Input Union proposal, which is currently being worked upon by the GraphQL Working Group, and there is a champion working on it.