Skip to content

Cannot return Interface from mutation #797

@axieum

Description

@axieum

Feature Request Type

  • Core functionality
  • Alteration (enhancement/optimization) of existing feature(s)
  • New behavior

Description

When returning an interface from a mutation like so -

@strawberry.type
class ProjectsMutation:
    """A collection of project-related GraphQL mutations."""

    update_external_project: ExternalProject = mutations.update_external_project(
        description="Updates an external project."
    )

    # ...

    # NB: The below mutations act on the `Project` interface. Since the mutations already return a union
    #     with `OperationInfo`, and interfaces are prohibited in unions, we must expand the full `Project` type.

    update_project: Project = mutations.update_project(
        description="Updates a generic project."
    )

    # ...

This raises an error that Interfaces cannot form part of a union.

https://spec.graphql.org/September2025/#sec-Unions.Type-Validation
"The member types of a Union type must all be Object base types; Scalar, Interface and Union types must not be > member types of a Union. Similarly, wrapping types must not be member types of a Union."

Image

I can see that the union.types both Project | OperationInfo, but since Project is an interface, this assertion fails.

Workaround

Explicitly type the mutation with concrete types rather than interface, e.g.

update_project: WebProject | ExternalProject | ... = mutations.update_project(
    description="Updates a generic project."
)

Solution

Detect when an interface is being unioned with the OperationInfo here:

types_ = tuple(get_possible_types(resolved))
if OperationInfo not in types_:
types_ = functools.reduce(operator.__or__, (*types_, OperationInfo))
name = capitalize_first(to_camel_case(self.python_name))
resolved = Annotated[
types_,
strawberry.union(f"{name}Payload"),
]
self.type_annotation = StrawberryAnnotation(
resolved,
namespace=getattr(self.type_annotation, "namespace", None),
)

And automatically spread any Interfaces in types_.

tl;dr - replace interface Project with its concrete types, e.g. WebProject | ExternalProject | ... | OperationInfo.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions