Description
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.