Union types

When designing your API, you may run into a situation where you want your field to resolve to one of a few possible types. It may be an error field that can resolve to one of many error types, or an activity feed made up of different types.

The most obvious solution may be creating a custom “intermediary” type that would define dedicated fields to different types:

type MutationPayload {
    status: Boolean!
    validationError: ValidationError
    permissionError: AccessError
    user: User
}

type FeedItem {
    post: Post
    image: Image
    user: User
}

GraphQL provides a dedicated solution to this problem in the form of dedicated Union type.

Union example

Consider an earlier error example. The union representing one of a possible three error types can be defined in schema like this:

union Error = NotFoundError | AccessError | ValidationError

This Error type can be used just like any other type:

type MutationPayload {
    status: Boolean!
    error: Error
    user: User
}

Your union will also need a special resolver named type resolver. This resolver will we called with an object returned from a field resolver and current context, and should return a string containing the name of an GraphQL type, or None if received type is incorrect:

def resolve_error_type(obj, *_):
    if isinstance(obj, ValidationError):
        return "ValidationError"
    if isinstance(obj, AccessError):
        return "AccessError"
    return None

Note

Returning None from this resolver will result in null being returned for this field in your query’s result. If field is not nullable, this will cause the GraphQL query to error.

Ariadne relies on dedicated UnionType class for bindinding this function to Union in your schema:

from ariadne import UnionType

error = UnionType("Error")

@error.type_resolver
def resolve_error_type(obj, *_):
    ...

If this function is already defined elsewhere (e.g. 3rd party package), you can instantiate the UnionType with it as second argument:

from ariadne import UnionType
from .graphql import resolve_error_type

error = UnionType("Error", resolve_error_type)

Lastly, your UnionType instance should be passed to make_executable_schema together will other types:

schema = make_executable_schema(type_defs, [query, error])

__typename field

Every type in GraphQL has a special __typename field that is resolved to a string containing the type’s name.

Including this field in your query may simplify implementation of result handling logic in your client:

query getFeed {
    feed {
        __typename
        ... on Post {
            text
        }
        ... on Image {
            url
        }
        ... on User {
            username
        }
    }
}

Assuming that the feed is a list, the query could produce the following response:

{
    "data": {
        "feed": [
            {
                "__typename": "User",
                "username": "Bob"
            },
            {
                "__typename": "User",
                "username": "Aerith"
            },
            {
                "__typename": "Image",
                "url": "http://placekitten.com/200/300"
            },
            {
                "__typename": "Post",
                "text": "Hello world!"
            },
            {
                "__typename": "Image",
                "url": "http://placekitten.com/200/300"
            }
        ]
    }
}

Client code could check the __typename value of every item in the feed to decide how it should be displayed in the interface.