---
description: Documentation on GraphQL Unions in Undine.
---

# 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`.
The `QueryTypes` in the `Union` should be added as generic type parameters to the `UnionType`.

```python
from undine import QueryType, UnionType

from .models import Project, Task


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


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


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

### Usage in Entrypoints

An `Entrypoint` created using a `UnionType` as the reference will return
all instances of all `QueryTypes` it contains.

```python
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:

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

### Filtering

By default, an `Entrypoint` for a `UnionType` will return all instances of all `QueryTypes` it contains.
However, if those `QueryTypes` implement a [`FilterSet`](filtering.md#filterset) or an
[`OrderSet`](ordering.md#orderset), those will also be available on the `UnionType` `Entrypoint`.

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

from .models import Project, Task


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


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


@TaskFilterSet
@TaskOrderSet
class TaskType(QueryType[Task]): ...


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


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


@ProjectFilterSet
@ProjectOrderSet
class ProjectType(QueryType[Project]): ...


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


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

This creates the following `Entrypoint`:

```graphql
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 `Union`, you can implement
a [`FilterSet`](filtering.md#filterset) or an [`OrderSet`](ordering.md#orderset)
for the same Models as the `QueryTypes` in the `UnionType` and add it to the `UnionType`.

```python
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]): ...


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


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

This creates the following `Entrypoint`:

```graphql
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`.
For example, a "name" `Filter` can be added to the `FilterSet` if all Models contain
a "name" field of type `CharField`.

#### Pagination

`UnionTypes` can be paginated just like any `QueryType`.

```python
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](pagination.md) section for more details on pagination.

### Schema name

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

```python
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.

```python
from undine import QueryType, UnionType

from .models import Project, Task


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


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


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

### Caching

You can set custom caching rules for `UnionTypes` using the `cache_time`
and `cache_per_user` arguments.

```python
from undine import QueryType, UnionType

from .models import Project, Task


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


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


class SearchObjects(UnionType[TaskType, ProjectType], cache_time=10, cache_per_user=True): ...
```

See the [Caching](caching.md) section for more details.

### Directives

You can add directives to the `UnionType` by providing them using the `directives` argument.
The directive must be usable in the `UNION` location.

```python
from graphql import DirectiveLocation

from undine import Directive, QueryType, UnionType

from .models import Project, Task


class MyDirective(Directive, locations=[DirectiveLocation.UNION]): ...


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


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


class SearchObjects(UnionType[TaskType, ProjectType], directives=[MyDirective()]): ...
```

You can also add directives using decorator syntax.

```python
from graphql import DirectiveLocation

from undine import Directive, QueryType, UnionType

from .models import Project, Task


class MyDirective(Directive, locations=[DirectiveLocation.UNION]): ...


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


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


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

See the [Directives](directives.md) section for more details on directives.

### GraphQL Extensions

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

```python
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 `Union` extensions
after the schema is created. The `UnionType` itself is found in the GraphQL `Union` extensions
under a key defined by the [`UNION_TYPE_EXTENSIONS_KEY`](settings.md#union_type_extensions_key)
setting.
