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

# Interfaces

In this section, we'll cover how GraphQL Interfaces work in Undine.
Interfaces are abstract GraphQL types that represent a group of fields
that an `ObjectType` can implement.

## InterfaceType

In Undine, a GraphQL Interface is implemented using the `InterfaceType` class
and defining a number of [`InterfaceFields`](#interfacefield) in its class body.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))
```

`QueryTypes` can implement `InterfaceTypes` by adding them to the `QueryType` using
the `interfaces` argument in their class definition.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType, QueryType

from .models import Task


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))


class TaskType(QueryType[Task], interfaces=[Named]): ...
```

You can also use decorator syntax to add an `InterfaceType` to a `QueryType`.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType, QueryType

from .models import Task


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))


@Named
class TaskType(QueryType[Task]): ...
```

Note that `InterfaceTypes` can also implement other `InterfaceTypes`.

```python
from graphql import GraphQLInt, GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))


class Person(InterfaceType, interfaces=[Named]):
    age = InterfaceField(GraphQLNonNull(GraphQLInt))
```

### Usage in Entrypoints

An `Entrypoint` created using an `InterfaceType` as the reference will return
all implementations of the `InterfaceType`.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import Entrypoint, InterfaceField, InterfaceType, QueryType, RootType

from .models import Step, Task


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))


class TaskType(QueryType[Task], interfaces=[Named]): ...


class StepType(QueryType[Step], interfaces=[Named]): ...


class Query(RootType):
    named = Entrypoint(Named, many=True)
```

This `Entrypoint` can be queried like this:

```graphql
query {
  named {
    name
    ... on TaskType {
      createdAt
    }
    ... on StepType {
      done
    }
    __typename
  }
}
```

#### Filtering

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

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import Entrypoint, FilterSet, InterfaceField, InterfaceType, OrderSet, QueryType, RootType

from .models import Step, Task


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))


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


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


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


class StepFilterSet(FilterSet[Step]): ...


class StepOrderSet(OrderSet[Step]): ...


@Named
@StepFilterSet
@StepOrderSet
class StepType(QueryType[Step]): ...


class Query(RootType):
    named = Entrypoint(Named, many=True)
```

This creates the following `Entrypoint`:

```graphql
type Query {
  named(
    filterTask: TaskFilterSet
    orderByTask: [TaskOrderSet!]
    filterStep: StepFilterSet
    orderByStep: [StepOrderSet!]
  ): [Named!]!
}
```

This allows filtering and ordering the different types of models in the `InterfaceType` separately.

To filter and order _across_ the different Models that implement the `InterfaceType`, you can implement
a [`FilterSet`](filtering.md#filterset) or an [`OrderSet`](ordering.md#orderset)
for the same Models as the `QueryTypes` implementing the `InterfaceType` and add it to the `InterfaceType`.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import Entrypoint, Filter, FilterSet, InterfaceField, InterfaceType, Order, OrderSet, QueryType, RootType

from .models import Step, Task


class NamedFilterSet(FilterSet[Task, Step], auto=False):
    name = Filter()
    name_contains = Filter(lookup="icontains", field_name="name")


class NamedOrderSet(OrderSet[Task, Step], auto=False):
    name = Order()


@NamedFilterSet
@NamedOrderSet
class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))


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


@Named
class StepType(QueryType[Step]): ...


class Query(RootType):
    named = Entrypoint(Named, many=True)
```

This creates the following `Entrypoint`:

```graphql
type Query {
  named(
    filter: NamedFilterSet
    orderBy: [NamedOrderSet!]
  ): [Named!]!
}
```

Note that a `FilterSet` or `OrderSet` created for multiple Models like this
should only contain `Filters` and `Orders` which will work on all Models that
implement the `InterfaceType`. For this reason, each `Filter` and `Order`
attached to the `InterfaceType` must reference a field declared as an
`InterfaceField` on the `InterfaceType` since those are the only fields guaranteed
to exist on all implementing `QueryTypes`.

#### Pagination

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

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import Entrypoint, InterfaceField, InterfaceType, QueryType, RootType
from undine.relay import Connection

from .models import Step, Task


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))


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


@Named
class StepType(QueryType[Step]): ...


class Query(RootType):
    named = Entrypoint(Connection(Named))
```

See the [Pagination](pagination.md) section for more details on pagination.

### Schema name

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

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType, schema_name="HasName"):
    name = InterfaceField(GraphQLNonNull(GraphQLString))
```

### Description

You can provide a description for the `InterfaceType` by adding a docstring to the class.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    """Description."""

    name = InterfaceField(GraphQLNonNull(GraphQLString))
```

### Caching

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

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType, cache_time=10, cache_per_user=True):
    name = InterfaceField(GraphQLNonNull(GraphQLString))
```

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

### Directives

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

```python
from graphql import DirectiveLocation, GraphQLNonNull, GraphQLString

from undine import Directive, InterfaceField, InterfaceType


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


class Named(InterfaceType, directives=[MyDirective()]):
    name = InterfaceField(GraphQLNonNull(GraphQLString))
```

You can also add directives using decorator syntax.

```python
from graphql import DirectiveLocation, GraphQLNonNull, GraphQLString

from undine import Directive, InterfaceField, InterfaceType


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


@MyDirective()
class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))
```

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

### GraphQL Extensions

You can provide custom extensions for the `InterfaceType` by providing an
`extensions` argument with a dictionary containing them. These can then be used
however you wish to extend the functionality of the `InterfaceType`.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType, extensions={"foo": "bar"}):
    name = InterfaceField(GraphQLNonNull(GraphQLString))
```

`InterfaceType` extensions are made available in the GraphQL `Interface` extensions
after the schema is created. The `InterfaceType` itself is found in the GraphQL `Interface` extensions
under a key defined by the [`INTERFACE_TYPE_EXTENSIONS_KEY`](settings.md#interface_type_extensions_key)
setting.

## InterfaceField

When a `QueryType` implements an `InterfaceType`, all of the `InterfaceFields` on
the `InterfaceType` are converted to `Fields` on the `QueryType`. The converted `Field` must
correspond to a Model field on the `QueryType` Model, and the `InterfaceField` output type
must match the GraphQL output type converted from Model field. In other words, all `InterfaceFields`
must correspond to Model fields when implemented on a `QueryType`.

An `InterfaceField` always requires its desired GraphQL output type to be defined.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))
```

Optionally, you can define arguments that the `InterfaceField` requires.
If defined, these must also match the Model field of the implementing `QueryType`.

```python
from graphql import GraphQLArgument, GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(
        GraphQLNonNull(GraphQLString),
        args={"name": GraphQLArgument(GraphQLNonNull(GraphQLString))},
    )
```

### Field name

By default, the name of the field in the Django model is the same as the name of the `InterfaceField`.
If you want to change the name of the field in the Django model separately,
you can do so by setting the `field_name` argument:

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString), field_name="name")
```

### Schema name

By default, the name of the `Interface` field generated from a `InterfaceField` is the same
as the name of the `InterfaceField` on the `InterfaceType` class (converted to _camelCase_ if
[`CAMEL_CASE_SCHEMA_FIELDS`](settings.md#camel_case_schema_fields) is enabled).
If you want to change the name of the `Interface` field separately,
you can do so by setting the `schema_name` argument:

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString), schema_name="name")
```

This can be useful when the desired name of the `Interface` field is a Python keyword
and cannot be used as the `Field` attribute name.

### Description

A description for a field can be provided in one of two ways:

1) By setting the `description` argument.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString), description="The name of the object.")
```

2) As class attribute docstrings, if [`ENABLE_CLASS_ATTRIBUTE_DOCSTRINGS`](settings.md#enable_class_attribute_docstrings) is enabled.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString))
    """The name of the object."""
```

### Deprecation reason

A `deprecation_reason` can be provided to mark the `InterfaceField` as deprecated.
This is for documentation purposes only, and does not affect the use of the `InterfaceField`.

```python hl_lines="13"
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString), deprecation_reason="Use `title` instead.")
```

### Caching

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

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString), cache_time=10, cache_per_user=True)
```

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

### Directives

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

```python
from graphql import DirectiveLocation, GraphQLNonNull, GraphQLString

from undine import Directive, InterfaceField, InterfaceType


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


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString), directives=[MyDirective()])
```

You can also add them using the `@` operator (which kind of looks like GraphQL syntax):

```python
from graphql import DirectiveLocation, GraphQLNonNull, GraphQLString

from undine import Directive, InterfaceField, InterfaceType


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


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString)) @ MyDirective()
```

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

### GraphQL Extensions

You can provide custom extensions for the `InterfaceField` by providing a
`extensions` argument with a dictionary containing them. These can then be used
however you wish to extend the functionality of the `InterfaceField`.

```python
from graphql import GraphQLNonNull, GraphQLString

from undine import InterfaceField, InterfaceType


class Named(InterfaceType):
    name = InterfaceField(GraphQLNonNull(GraphQLString), extensions={"foo": "bar"})
```

`InterfaceField` extensions are made available in the GraphQL `Interface` field extensions
after the schema is created. The `InterfaceField` itself is found in the GraphQL `Interface` field extensions
under a key defined by the [`INTERFACE_FIELD_EXTENSIONS_KEY`](settings.md#interface_field_extensions_key)
setting.
