I'm having a hard time understanding when to use GraphQLInterfaceType
and GraphQLUnionType
.
I've RTFMs:
Can anyone offer up a real world example when these would be useful to get it through my thick head?
Both are meant to help you design a schema with a heterogeneous set of types, and you could achieve the same functionality using both, but GraphQLInterfaceType
is more suitable when the types are basically the same but some of the fields are different, and GraphQLUnionType
when the types are totally different and have totally different fields.
Ultimately whether to use one or the other depending on your schema design.
For a real world example, let's say you have a list of blogs, but blogs using framework A use username and password as authentication, and blog using framework B use email and password. We design it with a GraphQLInterfaceType
like this:
const BlogType = new GraphQLInterfaceType({
name: 'Blog',
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
password: { type: new GraphQLNonNull(GraphQLString) }
},
resolveType: resolveBlogType
});
const BlogAType = new GraphQLObjectType({
name: 'BlogA',
interfaces: [Blog],
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
username: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const BlogBType = new GraphQLObjectType({
name: 'BlogB',
interfaces: [Blog],
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
email: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
function resolveBlogType(value) {
return value.username ? BlogAType : BlogBType;
}
When we create a new blog sending username
, it will create a BlogA
.
We can query like this:
query MyQuery {
blogs: {
url
password
... on BlogA {
email
}
... on BlogB {
username
}
}
}
Now let's get the same functionality but using GraphQLUnionType
, because we prefer to use simply one type of blog, and 2 types of authentication methods:
const AuthAType = new GraphQLObjectType({
name: 'AuthA',
fields: {
username: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const AuthBType = new GraphQLObjectType({
name: 'AuthB',
fields: {
email: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) }
}
});
const AuthType = new GraphQLUnionType({
name: 'Auth',
types: [AuthAType, AuthBType]
resolveType: resolveAuthType
});
const BlogType = new GraphQLInterfaceType({
name: 'Blog',
fields: {
url: { type: new GraphQLNonNull(GraphQLString) }
auth: { type: AuthType }
},
});
function resolveAuthType(value) {
return value.username ? AuthAType : AuthBType;
}
We can query like this:
query MyQuery {
blogs: {
url
auth {
... on AuthA {
username
password
}
... on AuthB {
email
password
}
}
}
}
As you can see in this example we achieve the same thing with the interface or the union, but one or the other might be more appropriate depending on your schema design.
For example, let's say you want to add a blog framework C that also use email and password. You would need to include another field to be able to differentiate it from blog framework B in our resolveBlogType
function. Let's add the type
field. In our Union example, since we only have access to the fields within the Union, you would to add type
to the Union. If in the future we wanted to add another Union with same fields for multiple frameworks, we would need to add the type
field there as well. Not so nice to have type
duplicated multiple times in our schema. It could be a better idea to use a Interface, and have a single type
field accessible at the resolveBlogType
function by all the Objects using the Interface.
The sematic of GraphQLInterfaceType
is like most program language's interface
. and graphql add some more behaviors for it. like check if the derived class implement all the fields,dynamic resolving to derived instance.
The sematic of GraphQLUnionType
is not a Union
,but something like OR
.(a little bit like the flowtype's type check?)
A real world example is example in Relay => Relay's Node design .GraphQLInterfaceType
is completely unrelated to GraphQLUnionType
.
I think maybe you was confused by this?
interface Node{
id: string
}
type Dog implements Node{
id: string
}
type Cat implements Node{
id: string
}
union Animal = Dog | Cat
type User{
node: Node
animal: Animal
}
If be confused by this, you should get some book of strong type language to read.(like C# or Java or something else. maybe you should have a look at Flow too, this usage Dog|Cat
is a type restriction)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With