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 an Entrypoint for a UnionType will return all instances of all 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 and ordering the different types of models in the UnionType separately.

To filter and order across different models in the UnionType, you can implement a FilterSet or an OrderSet for the same models as the UnionType and add it to the UnionType.

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

from .models import Project, Task


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


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


class SearchObjectsFilterSet(FilterSet[Task, Project]): ...


class SearchObjectsOrderSet(OrderSet[Task, Project]): ...


class SearchObjects(
    UnionType[TaskType, ProjectType],
    filterset=SearchObjectsFilterSet,
    orderset=SearchObjectsOrderSet,
): ...


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

This creates the following Entrypoint:

1
2
3
4
5
6
type Query {
  searchObjects(
    filter: SearchObjectsFilterSet
    orderBy: [SearchObjectsOrderSet!]
  ): [Commentable!]!
}

Note that a FilterSet or OrderSet created for multiple models like this should only contain Filters and Orders which will work on all models in the UnionType, i.e. they are of the same type.

Pagination๐Ÿ”—

To paginate UnionTypes, you can use the Connection Entrypoint.

from undine import Entrypoint, QueryType, RootType, UnionType
from undine.relay import Connection

from .models import Project, Task


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


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


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


class Query(RootType):
    search_objects = Entrypoint(Connection(SearchObjects))

See the Pagination section for more details on pagination.

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 UNION_TYPE_EXTENSIONS_KEY setting.