-
-
Notifications
You must be signed in to change notification settings - Fork 603
Description
In my queries/mutations I'm returning sqlalchemy objects. The GQL types might expose the entire set of fields or a subset. We usually have 1-1, but we might get a GQL type that resolves fields from multiple sqlalchemy models.
For example, when I build a Page object, I have
return Page[Product](items=[SQLProduct(), SQLProductt()])If I define the output of an operation like
products: Page[Product] | Error = strawberry.field(resolver=get_products)Then I get an error saying the root is not a type of the union possible types.
If I define the query without returning an Union it works fine
products: Page[Product] = strawberry.field(resolver=get_products)My guess is that somewhere in the resolution it is not resolving the type properly, so in the end i get the error
"Page is not in the union types [Product, Error]"
Context
I have a base class for all my gql types, where I configure the mapping to our sql types
where I implement is_type_of. Here i check that the obj param matches with my mapping. This allows me to pass sqlobjects to resolvers that expect object types. That's something we have been doing with graphene for ages, but found abit more challenging with strawberry because of all the type checking, but I feel like its how gql was meant to be. Otherwise I would have to convert all my results to gql types before returning them, which kinda defeats the purpose of having the field resolvers.
It looks something like this
@strawberry.type
class BaseGQLType:
"""
Base type for all GQL strawberry types.
"""
__domain_type__: ClassVar[type | None] = None
@classmethod
def is_type_of(cls, obj: Any, _info: GraphQLResolveInfo) -> bool:
if (cls == obj.__class__) or (cls.__domain_type__ and isinstance(obj, cls.__domain_type__)):
return True
return super().is_type_of(obj, _info) if hasattr(super(), "is_type_of") else False # type: ignore[misc]So my GQL Product type would look like
@strawberry.type
class Product(BaseGQLType):
__domain_type__ : strawberry.Private[type| None] = SQLProduct
name: str
price: DecimalAnd my query would look like
@strawberry.type
class Error(BaseGQLType):
message: str
@strawberry.type
class Page(Generic[T], BaseGQLType):
items: list[T]
def get_products() -> Page[Product] | Error:
result = fetch_sql_products()
return Page[Product](items=result) # <== here's where we return a sql object instead of a gql object type
@strawberry.type
class Query:
products: Page[Product] | Error = strawberry.field(resolver=get_products)If I change to just return the generic Page (no union with error) it works
def get_products() -> Page[Product]:
result = fetch_sql_products()
return Page[Product](items=result)
@strawberry.type
class Query:
products: Page[Product] = strawberry.field(resolver=get_products)I have also noticed that if I pass items as [] then it works fine
# Any of this two work as well
def get_products() -> Page[Product] | Error:
result = fetch_sql_products()
return Page[Product](items=[])So it looks like there's an issue with sending other than the actual gql type in the result when using unions of generics.
System Information
- Operating system: ubuntu 24.04
- Python version: 3.12
- Strawberry version (if applicable): 0.283.3