Skip to content

Automatic object type resolution does not trigger in reference resolvers #3809

Open
@parafoxia

Description

@parafoxia

Describe the Bug

This is a bit of a tricky one to explain, so do let me know if any further clarity is needed.

TL;DR: Strawberry is unable to resolve federated types from other object types or entities where the reference resolver does not explicitly return an object type, or where the type being federated defines inline field resolvers.

It's easier to explain with an MRE. This system has two services, groups and users, each pertaining to group and user queries respectively. The Group type is federated, and the users service has two queries. Users contains group as a field.

groups/app.py:

from types import SimpleNamespace
from typing import Self

import strawberry

groups = {
    "1": SimpleNamespace(id="1", name="Hello", altname="Hey"),
    "2": SimpleNamespace(id="2", name="Strawberry"),
    "3": SimpleNamespace(id="3", name="World", altname="Earth"),
}


@strawberry.federation.type(keys=["id"])
class Group:
    id: strawberry.ID
    name: str
    altname: str = strawberry.field(
        resolver=lambda root: getattr(root, "altname", root.name),
    )

    @classmethod
    def resolve_reference(cls, id: str) -> Self:
        return groups.get(id)


schema = strawberry.federation.Schema(
    types=[Group],
    enable_federation_2=True,
)

users/app.py:

from types import SimpleNamespace

import strawberry

users = {
    "1": SimpleNamespace(id="1", group_id="1"),
    "2": SimpleNamespace(id="2", group_id="2"),
    "3": SimpleNamespace(id="3", group_id="3"),
}


@strawberry.federation.type(keys=["id"])
class Group:
    id: strawberry.ID


@strawberry.type
class User:
    id: int
    group: Group = strawberry.field(
        resolver=lambda root: Group(id=root.group_id),
    )


@strawberry.type
class Query:
    @strawberry.field
    def users(self) -> list[User]:
        return list(users.values())

    @strawberry.field
    def user(self) -> User:
        return users.get("1")


schema = strawberry.federation.Schema(
    query=Query,
    enable_federation_2=True,
)

Posting the following query (altname is intentionally omitted for now):

{"query": "query { users { id group { id name } } }"

returns the following error in the groups service:

GraphQL request:1:37
1 | query($representations: [_Any!]!) { _entities(representations: $representations) { ... on Group { name } } }
  |                                     ^
Traceback (most recent call last):
  File "/Users/ethanhenderson/Programs/Playground/strawberry_test/.venv/lib/python3.13/site-packages/graphql/execution/execute.py", line 728, in complete_list_value
    completed_item = self.complete_value(
        item_type, field_nodes, info, item_path, item
    )
  File "/Users/ethanhenderson/Programs/Playground/strawberry_test/.venv/lib/python3.13/site-packages/graphql/execution/execute.py", line 646, in complete_value
    return self.complete_abstract_value(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        cast(GraphQLAbstractType, return_type), field_nodes, info, path, result
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/Users/ethanhenderson/Programs/Playground/strawberry_test/.venv/lib/python3.13/site-packages/graphql/execution/execute.py", line 798, in complete_abstract_value
    runtime_type = resolve_type_fn(result, info, return_type)
  File "/Users/ethanhenderson/Programs/Playground/strawberry_test/.venv/lib/python3.13/site-packages/strawberry/types/union.py", line 185, in _resolve_union_type
    raise WrongReturnTypeForUnion(info.field_name, str(type(root)))
strawberry.exceptions.WrongReturnTypeForUnion: The type "<class 'types.SimpleNamespace'>" cannot be resolved for the field "_entities" , are you using a strawberry.field?

This error can be resolved by explicitly returning a Group object type, like so:

@classmethod
def resolve_reference(cls, id: str) -> Self:
    group = groups.get(id)
    return Group(id=group.id, name=group.name)

However, when querying altname as well, which uses an inline resolver:

{"query": "query { users { id group { id name altname } } }"

The groups service raises this error instead:

GraphQL request:1:104
1 | query($representations: [_Any!]!) { _entities(representations: $representations) { ... on Group { name altname } } }
  |                                                                                                        ^
Traceback (most recent call last):
  File "/Users/ethanhenderson/Programs/Playground/strawberry_test/.venv/lib/python3.13/site-packages/graphql/execution/execute.py", line 542, in execute_field
    completed = self.complete_value(
        return_type, field_nodes, info, path, result
    )
  File "/Users/ethanhenderson/Programs/Playground/strawberry_test/.venv/lib/python3.13/site-packages/graphql/execution/execute.py", line 614, in complete_value
    completed = self.complete_value(
        cast(GraphQLNonNull, return_type).of_type,
    ...<3 lines>...
        result,
    )
  File "/Users/ethanhenderson/Programs/Playground/strawberry_test/.venv/lib/python3.13/site-packages/graphql/execution/execute.py", line 641, in complete_value
    return self.complete_leaf_value(cast(GraphQLLeafType, return_type), result)
           ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ethanhenderson/Programs/Playground/strawberry_test/.venv/lib/python3.13/site-packages/graphql/execution/execute.py", line 776, in complete_leaf_value
    serialized_result = return_type.serialize(result)
  File "/Users/ethanhenderson/Programs/Playground/strawberry_test/.venv/lib/python3.13/site-packages/graphql/type/scalars.py", line 177, in serialize_string
    raise GraphQLError("String cannot represent value: " + inspect(output_value))
graphql.error.graphql_error.GraphQLError: String cannot represent value: <method>

altname can't be passed as an argument to the constructor as it has an inline resolver, and it can't be resolved when being federated from another type as Strawberry doesn't perform the necessary resolution. This makes it very difficult to use inline resolvers in federated types.

If there's another way of doing this I'm missing, do let me know.

System Information

  • Operating system: MacOS 15.3.2
  • Strawberry version (if applicable): 0.262.3

Additional Context

The MRE is based on the setup in the Federation v2 Guide to try and make it simpler.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions