I am working with a native graph database.
My data objects are composed from many smaller sets of attributes, and I would like to be able to query all of them at once without adding extra artificial layers in my schema. The attributes are very well defined, but the composition of them is not.
Assume it is easy (trivial) for me to output this object from a query:
{
"name": "Zapdos",
"wingspan": 40,
"airspeed": 120,
"maxVoltage": 100000000
}
Assume that the wingspan
and airVelocity
properties come from a FlyingType
attribute, and maxVoltage
from an ElectricType
attribute. These and other attributes are well defined. Other pokemon may mix and match attributes that apply to them, and defining all combinations of them might not make sense.
Is it possible to run the following query and get data in the same format back?
Would graphql-js
have to handle an array returned by resolveType
function? Does the default resolveType
function stop at the first true value from calling isTypeOf
?
query {
Pokemon(name: "Zapdos") {
name
... on isFlyingType {
wingspan
airVelocity
}
... on isElectricType {
maxVoltage
}
}
}
Do I actually need to build a new type with everything that I need? I have many attributes that compose my data, and that would mean A LOT of types to account every combination of interfaces. E.g. must I do the following?
type FlyingElectricPokemon implements isPokemon, isFlyingType, isElectricType {
name: String!
wingspan: Int
airVelocity: Int
maxVoltage: Int
}
query {
Pokemon(name: "Zapdos") {
name
... on FlyingElectricPokemon {
wingspan
airVelocity
maxVoltage
}
}
}
Am I just fighting too hard to avoid the strong typing of GraphQL on my rather weakly-typed graph data?
I would prefer to avoid the following add complexity:
type Query {
Pokemon(name: String): Pokemon
}
type Pokemon {
name: String!
attributes: [isAttribute!]
}
interface isAttribute {
type: String!
}
type FlyingType implements isAttribute {
type: String!
wingspan: Int
}
type ElectricType implements isAttribute {
type: String!
maxVoltage: Int
}
query {
Pokemon(name: "Zapdos") {
name
attributes {
... on FlyingType {
wingspan
airVelocity
}
... on ElectricType {
maxVoltage
}
}
}
}
requiring data in the following format
{
"name": "Zapdos",
"attributes": [
{ "type": "flying", "wingspan": 40 },
{ "type": "electric", "maxVoltage": 100000000 }
]
}
This latter bit I actually got to work on Launchpad.
That's an interesting scenario. I don't think vanilla GraphQL really offers a vehicle to achieve what you describe. The problem is that, in the end, any object being returned has to have exactly one type. __resolveType
cannot return an array, and even if you declare a __isTypeof
for multiple types that will evaluate true for a given object, only the first one that evaluates true will be associated with that particular object.
At the end of the day, if your intent is to return a subset of possible attributes (based on the Pokemon's type or types), the simplest solution may be to just make all possible attributes into fields of the Pokemon
type and not worry about trying to implement interfaces around the types. This achieves the flat data structure you want in your response. The biggest downside is you will have a possibly large number of null fields returned as part of the response.
You can also look into something like graphql-s2s, which supports type inheritance and generics.