Repository: strawberry-graphql/strawberry

Issue:
Add support for interfaces
We should be able to define interfaces with strawberry, something like this:

```python

@strawberry.interface
class Node:
    id: strawberry.ID
```


Discussion:

Resolving patch:
diff --git a/strawberry/__init__.py b/strawberry/__init__.py
--- a/strawberry/__init__.py
+++ b/strawberry/__init__.py
@@ -6,4 +6,4 @@
 from .mutation import mutation, subscription  # noqa
 from .scalars import ID  # noqa
 from .schema import Schema  # noqa
-from .type import input, type  # noqa
+from .type import input, type, interface  # noqa
diff --git a/strawberry/constants.py b/strawberry/constants.py
--- a/strawberry/constants.py
+++ b/strawberry/constants.py
@@ -1,2 +1,3 @@
 IS_STRAWBERRY_FIELD = "_is_strawberry_field"
 IS_STRAWBERRY_INPUT = "_is_strawberry_input"
+IS_STRAWBERRY_INTERFACE = "_is_strawberry_interface"
diff --git a/strawberry/type.py b/strawberry/type.py
--- a/strawberry/type.py
+++ b/strawberry/type.py
@@ -6,11 +6,12 @@
     GraphQLField,
     GraphQLInputField,
     GraphQLInputObjectType,
+    GraphQLInterfaceType,
     GraphQLObjectType,
 )
 from graphql.utilities.schema_printer import print_type
 
-from .constants import IS_STRAWBERRY_FIELD, IS_STRAWBERRY_INPUT
+from .constants import IS_STRAWBERRY_FIELD, IS_STRAWBERRY_INPUT, IS_STRAWBERRY_INTERFACE
 from .type_converter import REGISTRY, get_graphql_type_for_annotation
 from .utils.str_converters import to_camel_case
 
@@ -51,7 +52,7 @@ def _convert_annotations_fields(cls, *, is_input=False):
     return fields
 
 
-def _process_type(cls, *, is_input=False, description=None):
+def _process_type(cls, *, is_input=False, is_interface=False, description=None):
     name = cls.__name__
     REGISTRY[name] = cls
 
@@ -75,16 +76,30 @@ def _get_fields():
 
     if is_input:
         setattr(cls, IS_STRAWBERRY_INPUT, True)
+    elif is_interface:
+        setattr(cls, IS_STRAWBERRY_INTERFACE, True)
 
     extra_kwargs = {"description": description or cls.__doc__}
 
-    TypeClass = GraphQLInputObjectType if is_input else GraphQLObjectType
+    if is_input:
+        TypeClass = GraphQLInputObjectType
+    elif is_interface:
+        TypeClass = GraphQLInterfaceType
+    else:
+        TypeClass = GraphQLObjectType
+
+        extra_kwargs["interfaces"] = [
+            klass.field
+            for klass in cls.__bases__
+            if hasattr(klass, IS_STRAWBERRY_INTERFACE)
+        ]
+
     cls.field = TypeClass(name, lambda: _get_fields(), **extra_kwargs)
 
     return dataclass(cls, repr=False)
 
 
-def type(cls=None, *, is_input=False, description=None):
+def type(cls=None, *, is_input=False, is_interface=False, description=None):
     """Annotates a class as a GraphQL type.
 
     Example usage:
@@ -95,7 +110,9 @@ def type(cls=None, *, is_input=False, description=None):
     """
 
     def wrap(cls):
-        return _process_type(cls, is_input=is_input, description=description)
+        return _process_type(
+            cls, is_input=is_input, is_interface=is_interface, description=description
+        )
 
     if cls is None:
         return wrap
@@ -104,3 +121,4 @@ def wrap(cls):
 
 
 input = partial(type, is_input=True)
+interface = partial(type, is_interface=True)


Test patch:
diff --git a/tests/test_interface.py b/tests/test_interface.py
new file mode 100644
--- /dev/null
+++ b/tests/test_interface.py
@@ -0,0 +1,36 @@
+import textwrap
+
+import strawberry
+
+
+def test_interface():
+    @strawberry.interface
+    class Node:
+        id: strawberry.ID
+
+    expected_representation = """
+    interface Node {
+      id: ID!
+    }
+    """
+
+    assert repr(Node("a")) == textwrap.dedent(expected_representation).strip()
+
+
+def test_implementing_interface():
+    @strawberry.interface
+    class Node:
+        id: strawberry.ID
+
+    @strawberry.type
+    class Post(Node):
+        title: str
+
+    expected_representation = """
+    type Post implements Node {
+      id: ID!
+      title: String!
+    }
+    """
+
+    assert repr(Post("a", "abc")) == textwrap.dedent(expected_representation).strip()