Model Unions as sparse fields in GraphQL
Problem
The GraphQL API does not support Union types, yet you need query results that are comparable to using Unions.
This recipe is one solution to the problem outlined in Composable Types without Unions.
Solution
One method to model a union is to provide all fields for all entities on
the same type. This is especially useful when the various entities share
many properties. When creating or updating an entity of a given type,
only the fields related to that type are provided, while the rest remain
null
. You can provide a String or Enum "type" or "tag" field that
identifies the entity as a certain type of item.
Setup
-
Update your GraphQL schema to include:
type User { name: String! favorites: [MediaItem]! @relation # ... } enum MediaType { MOVIE SHOW BOOK } type MediaItem { type: MediaType! title: String! newRelease: Boolean # ...movie fields # ...show fields # ...book fields favoritedBy: [User]! @relation } type Query { itemsByType(type: MediaType!): [MediaItem]! itemsByNewRelease(newRelease: Boolean!): [MediaItem]! }
This involves editing the GraphQL schema definition, wherever you have stored it, and then uploading the new schema in the Fauna Dashboard.
-
Run GraphQL queries to create a
User
and someMediaItem
s:mutation { createUser( data: { name: "Alice" favorites: { create: [ { title: "Black Widow", type:MOVIE, newRelease: true } { title: "Psycho", type:MOVIE } { title: "The Expanse", type:SHOW, newRelease: true } { title: "Fellowship of the Ring", type:BOOK } ] } } ) { name _id favorites { data { title, type, newRelease, _id } } } }
The result should be similar to:
{ "data": { "createUser": { "name": "Alice", "_id": "332200338156159522", "favorites": { "data": [ { "title": "Black Widow", "type": "MOVIE", "newRelease": true, "_id": "332200338158256674" }, { "title": "Psycho", "type": "MOVIE", "newRelease": null, "_id": "332200338163499554" }, { "title": "The Expanse", "type": "SHOW", "newRelease": true, "_id": "332200338166645282" }, { "title": "Fellowship of the Ring", "type": "BOOK", "newRelease": null, "_id": "332200338170839586" } ] } } } }
The document IDs presented in the _id
fields are different for every new document created in Fauna. The Objective 3 query requires two of the document IDs present in this result.
Objective 1: Query for all entities of a given type
You can get all items of a type by indexing on a "type" or "tag" field.
Indexes are automatically added by the GraphQL API using the fields
specified on the Query type to support search by fields, such as the
type
field:
{
allMovies: itemsByType(type: MOVIE) {
data { title, newRelease }
}
allShows: itemsByType(type: SHOW) {
data { title, newRelease }
}
allBooks: itemsByType(type: BOOK) {
data { title, newRelease }
}
}
You should see the result:
{
"data": {
"allMovies": {
"data": [
{
"title": "Black Widow",
"newRelease": true
},
{
"title": "Psycho",
"newRelease": null
}
]
},
"allShows": {
"data": [
{
"title": "The Expanse",
"newRelease": true
}
]
},
"allBooks": {
"data": [
{
"title": "Fellowship of the Ring",
"newRelease": null
}
]
}
}
}
Objective 2: Query for all entities with certain properties
Every entity is searchable by every property. Indexes are automatically
added by the GraphQL API using the fields specified on the Query type
to support search-by-field, such as the newRelease
field:
{
itemsByNewRelease(newRelease: true) {
data { title, type, newRelease }
}
}
The result should be:
{
"data": {
"itemsByNewRelease": {
"data": [
{
"title": "Black Widow",
"type": "MOVIE",
"newRelease": true
},
{
"title": "The Expanse",
"type": "SHOW",
"newRelease": true
}
]
}
}
}
Objective 3: Query for all relationships of an entity, regardless of which type it points to
-
You can search for all user favorites by selecting the single
favorites
field. -
You can search for all users that have marked the media item as a favorite through the
favoritedBy
field.
To make the following query work:
-
replace
<USERID>
with the document ID for the user created in the setup’s step 2 (just below thename
field in the result). -
replace
<MEDIAITEMID>
with the document ID for the "Black Widow" movie created in the setup’s step 2.
{
findUserByID(id: "<USERID>") {
name
favorites {
data { title, type, newRelease }
}
}
findMediaItemByID(id: "<MEDIAITEMID>") {
title
type
newRelease
favoritedBy {
data { name }
}
}
}
The result should be similar to:
"data": {
"findUserByID": {
"name": "Alice",
"favorites": {
"data": [
{
"title": "Black Widow",
"type": "MOVIE",
"newRelease": true
},
{
"title": "Psycho",
"type": "MOVIE",
"newRelease": null
},
{
"title": "The Expanse",
"type": "SHOW",
"newRelease": true
},
{
"title": "Fellowship of the Ring",
"type": "BOOK",
"newRelease": null
}
]
}
},
"findMediaItemByID": {
"title": "Black Widow",
"type": "MOVIE",
"newRelease": true,
"favoritedBy": {
"data": [
{
"name": "Alice"
}
]
}
}
}
}
Is this article helpful?
Tell Fauna how the article can be improved:
Visit Fauna's forums
or email docs@fauna.com
Thank you for your feedback!