Unions🔗

In this section, we'll cover how GraphQL Unions work in Undine. Unions are abstract GraphQL types that represent a group of ObjectTypes that need to be returned together, e.g. for a search result.

UnionType🔗

In Undine, a GraphQL Union between two or more QueryTypes is implemented using a UnionType.

from undine import QueryType, UnionType

from .models import Project, Task


class TaskType(QueryType[Task]): ...


class ProjectType(QueryType[Project]): ...


class SearchObjects(UnionType[TaskType, ProjectType]): ...

A UnionType can be added to a schema using an Entrypoint. Note that UnionType should always be added using a list Entrypoint (e.g. many=True).

from undine import Entrypoint, QueryType, RootType, UnionType

from .models import Project, Task


class TaskType(QueryType[Task]): ...


class ProjectType(QueryType[Project]): ...


class SearchObjects(UnionType[TaskType, ProjectType]): ...


class Query(RootType):
    search_objects = Entrypoint(SearchObjects, many=True)

This Entrypoint can be queried like this:

query {
  searchObjects {
    __typename
    ... on TaskType {
      name
    }
    ... on ProjectType {
      name
    }
  }
}

Filtering🔗

By default, the UnionType will return all instances of the QueryTypes it contains. However, if those QueryTypes implement a FilterSet or an OrderSet, those will also be available on the UnionType Entrypoint.

from undine import Entrypoint, FilterSet, OrderSet, QueryType, RootType, UnionType

from .models import Project, Task


class TaskFilterSet(FilterSet[Task]): ...


class TaskOrderSet(OrderSet[Task]): ...


class TaskType(QueryType[Task], filterset=TaskFilterSet, orderset=TaskOrderSet): ...


class ProjectFilterSet(FilterSet[Project]): ...


class ProjectOrderSet(OrderSet[Project]): ...


class ProjectType(QueryType[Project], filterset=ProjectFilterSet, orderset=ProjectOrderSet): ...


class SearchObjects(UnionType[TaskType, ProjectType]): ...


class Query(RootType):
    search_objects = Entrypoint(SearchObjects, many=True)

This creates the following Entrypoint:

1
2
3
4
5
6
7
8
type Query {
  searchObjects(
    filterTask: TaskFilterSet
    orderByTask: [TaskOrderSet!]
    filterProject: ProjectFilterSet
    orderByProject: [ProjectOrderSet!]
  ): [Commentable!]!
}

This allows filtering the different types of models in the UnionType separately.

The UnionType also provides a __process_results__ method that can be used to filter the results of the union after everything has been fetched.

from undine import GQLInfo, QueryType, UnionType

from .models import Project, Task


class TaskType(QueryType[Task]): ...


class ProjectType(QueryType[Project]): ...


class SearchObjects(UnionType[TaskType, ProjectType]):
    @classmethod
    def __process_results__(cls, instances: list[Task | Project], info: GQLInfo) -> list[Task | Project]:
        return sorted(instances, key=lambda i: i.name)

By default, the number of items returned is limited per model in the UnionType. This is set by the ENTRYPOINT_LIMIT_PER_MODEL setting, but can also be changed per Entrypoint using the limit argument:

from undine import Entrypoint, QueryType, RootType, UnionType

from .models import Project, Task


class TaskType(QueryType[Task]): ...


class ProjectType(QueryType[Project]): ...


class SearchObjects(UnionType[TaskType, ProjectType]): ...


class Query(RootType):
    search_objects = Entrypoint(SearchObjects, many=True, limit=10)
What about pagination?

Pagination of UnionTypes is not supported yet.

Schema name🔗

By default, the name of the generated GraphQL Union is the same as the name of the UnionType class. If you want to change the name, you can do so by setting the schema_name argument:

from undine import QueryType, UnionType

from .models import Project, Task


class TaskType(QueryType[Task]): ...


class ProjectType(QueryType[Project]): ...


class SearchObjects(UnionType[TaskType, ProjectType], schema_name="Search"): ...

Description🔗

A description for a UnionType can be provided as a docstring.

from undine import QueryType, UnionType

from .models import Project, Task


class TaskType(QueryType[Task]): ...


class ProjectType(QueryType[Project]): ...


class SearchObjects(UnionType[TaskType, ProjectType]):
    """Description"""

GraphQL Extensions🔗

You can provide custom extensions for the UnionType by providing a extensions argument with a dictionary containing them.

from undine import QueryType, UnionType

from .models import Project, Task


class TaskType(QueryType[Task]): ...


class ProjectType(QueryType[Project]): ...


class SearchObjects(UnionType[TaskType, ProjectType], extensions={"foo": "bar"}): ...

UnionType extensions are made available in the GraphQL UnionType extensions after the schema is created. The UnionType itself is found in the extensions under a key defined by the QUERY_TYPE_UNION_EXTENSIONS_KEY setting.