This is different from #38 or #566.
I’m working on an integration to stitch a schema generated by Strawberry into a larger (Dagger) API, but Strawberry fails validation because of unknown types early during instantiation. I’m looking for a cleaner way to leverage Strawberry to do this, with minimal knowledge of Strawberry’s internals (for better future proofing).
The merging/stitching isn’t done by Strawberry, it’s done by the Dagger router. There’s different SDKs supporting multiple different languages and each of those extend the API with their own sub-schema. Only the Python SDK uses Strawberry.
I need to use types from the API like a dagger.Directory, it looks like this:
import dagger
from dagger.server import command
@command
def build(client: dagger.Client) -> dagger.Directory:
return client.directory()
Since I have a thin wrapper over Strawberry, I can transform input/output types between Dagger and Strawberry. So the above generates something like this:
from dataclasses import dataclass
import strawberry
@dataclass
class Directory:
id: str
@strawberry.type(extend=True)
class Query:
@strawberry.field
def build(self) -> Directory:
return Directory(id="foobar")
schema = strawberry.Schema(query=Query)
print(schema)
The problem is that the Directory
type already exists in the API where this is going to be stitched, and it’s not a federated API, so if I don’t declare it in Strawberry, it fails during schema validation:
TypeError: Query fields cannot be resolved. Unexpected type '<class '__main__.Directory'>'
It’s not a problem if the schema is invalid from Strawberry’s point of view because I only need to generate a “partial” schema to be stitched and to have a mapping of resolver functions from a Type.field
type string. Strawberry isn’t being used to execute queries directly.
I spent quite a bit of time on the codebase to find some way to integrate more cleanly here. I looked at this part:
strawberry/strawberry/schema/schema.py
Line 133 in be5f8c6
To see if it was possible to just return a GraphQLNamedType("Directory")
that would skip any Strawberry specific validation and allow generating this “invalid” schema:
extend type Query {
build: Directory!
}
That’s what the Go SDK does, but that SDK is generating the lower level objects “manually” while I’m trying to leverage Strawberry to do most of that work:
https://github.com/dagger/dagger/blob/46935c5813bec92b5bccb8253a527d64c9b78dec/sdk/go/server.go#L720
I could extend the schema_converter
to do that myself, but there’s no way to replace it with another implementation:
strawberry/strawberry/schema/schema.py
Line 103 in be5f8c6
That validation is happening in Schema.__init__
, so I can’t instantiate, replace schema_converter
attribute and then go through validation.
One possibility here could be:
# strawberry/schema/schema.py
class Schema(BaseSchema):
schema_converter_class = GraphQLCoreConverter
def __init__(self, ...):
...
self.schema_converter = self.schema_converter_class(self.config, scalar_registry)
...
# dagger/server/_schema.py
class DaggerSchema(strawberry.Schema):
schema_converter_class = DaggerSchemaConverter
# dagger/server/_commands.py
schema = DaggerSchema(query=root)
So the workaround I’m using now is to create a dummy type and deleting it afterwards from the internal GraphQLSchema
that Strawberry produces:
Which is equivalent to doing this:
- from dataclasses import dataclass
import strawberry
- @dataclass
+ @strawberry.type
class Directory:
id: str
@strawberry.type(extend=True)
class Query:
@strawberry.field
def build(self) -> Directory:
return Directory(id="foobar")
schema = strawberry.Schema(query=Query)
+ del schema._schema.type_map["Directory"]
print(schema)
The problem with this workaround is accessing a private attribute (i.e., schema._schema
). And if it was easier to go around the initial validation it would be better not to have to add the dummy types and removing them later.
To recap, this is what I'm trying to generate:
extend type Query {
build: Directory!
}
\cc @patrick91, following up on a Discord convo.
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too