Union types
|
This is the documentation of the GraphQL Library version 7. For the long-term support (LTS) version 5, refer to GraphQL Library version 5 LTS. |
This page describes how to use unions in the context of relationships, and exemplifies how to use them in queries and mutations.
Data model
Take the following graph as an example in which a User type has a relationship HAS_CONTENT to a Content type, which is a union of two node types, Blog and Post.
Sample database data
Consider a database consisting of the following sample data, which follows the data model described above.
Writing type definitions
Conceptually, both Blog and Post are types of "content" attributed to a User.
However they are fundamentally different types of content with different fields therefore they can be grouped together by a union type.
union Content = Blog | Post
type Blog @node {
title: String
posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT)
}
type Post @node {
content: String
}
type User @node {
name: String
content: [Content!]! @relationship(type: "HAS_CONTENT", direction: OUT)
}
Querying a union
Union types are useful for modeling relationships that can point to multiple different node types which do not share common fields.
When querying a relationship field of a union type, you must use inline fragments to specify which fields to return for each type in the union.
Notice the use of inline fragments for all the member types of the Content union, which are Blog and Post in this case.
query {
users {
name
content {
... on Blog {
title
}
... on Post {
content
}
}
}
}
If the inline fragment corresponding to a union member type is not included in the query, then no fields of that type are returned. Instead the response will be an empty object.
The following query only includes an inline fragment for the Blog type, and omits the inline fragment for the Post type:
query {
users {
name
content {
... on Blog {
title
}
}
}
}
The response to this query includes empty objects for the Post type, as no fields were specified for it in the query:
{
"data": {
"users": [
{
"name": "Alice",
"content": [{ "title": "Alice's Blog" }, {}]
},
{
"name": "Bob",
"content": [{}]
}
]
}
}
Filtering for a specific union member type
To filter for a specific union member type, you can use the where argument on the relationship field.
Notice the inline fragment corresponding to the Post type does not need to be included in the query, as they are filtered out by the where argument:
query {
users {
name
content(where: { Blog: { NOT: { title: { eq: null } } }}) {
... on Blog {
title
}
}
}
}
Creating a union relationship
To create the sample database data shown in the graph above, one approach is to first create the User nodes.
User Alice is connected through the HAS_CONTENT relationship to a Blog node with two Post nodes connected to it through the HAS_POST relationship.
The relationship to the Content union can be created through a nested mutation at the same time.
Notice the specification of the concrete type (Blog) of the Content union in the create input.
mutation {
createUsers(
input: [
{
name: "Bob"
},
{
name: "Alice"
content: {
Blog: {
create: [
{
node: {
title: "Our Blog"
posts: {
create: [
{ node: { content: "Alice's Post" } },
{ node: { content: "Bob's Post" } }
]
}
}
}
]
}
}
}
]
) {
users {
name
}
}
}
Executing this mutation on an empty database creates the following graph:
Update a union relationship
You can update the User nodes and create their HAS_POST relationships to the Post nodes part of the Content union.
Notice the specification of the concrete type (Post) of the Content union in the update input.
mutation {
mutation {
updateUsers(
where: { name: { eq: "Alice" } }
update: {
content: {
Post: [
{
connect: [
{
where: { node: { content: { eq: "Alice's Post" } } }
}
]
}
]
}
}
) {
users {
name
}
}
}
}
Execute the same mutation again for User Bob, adjusting the filter criteria.