-
-
Notifications
You must be signed in to change notification settings - Fork 535
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for supported_schemas
#2609
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #2609 +/- ##
==========================================
- Coverage 96.48% 96.02% -0.46%
==========================================
Files 467 471 +4
Lines 29166 29395 +229
Branches 3592 3623 +31
==========================================
+ Hits 28140 28227 +87
- Misses 843 975 +132
- Partials 183 193 +10 |
Thanks for adding the Here's a preview of the changelog: This release adds an optional One can decide to limit the support for a particular field to:
When the GraphQL schema is being built, we may pass an identifier for the current Examples of field definitions: import strawberry
from strawberry.field import SupportedSchema
@strawberry.type
class User:
# [...]
@strawberry.field(name="debugMessage", supported_schemas=[
SupportedSchema(name="internal"),
])
def get_debug_message(self) -> str:
# This field will only appear in the schemas called `internal`
# [...]
@strawberry.field(name="riskScore", supported_schemas=[
SupportedSchema(name="internal", until_version="1.2"),
])
def get_old_risk_score(self) -> float:
# This field will only appear in the schemas called `internal` that have
# a version lower than or equal to `1.2`
# [...]
@strawberry.field(name="riskScore", supported_schemas=[
SupportedSchema(name="internal", from_version="1.3"),
])
def get_new_risk_score(self) -> float:
# This field will only appear in the schemas called `internal` that have
# a version higher than or equal to `1.3`
# [...] Examples of schema definition: import strawberry
from strawberry.schema.identifier import SchemaIdentifier
# [...] Define `Query` and `Mutation`
internal_schema = strawberry.Schema(
query=Query,
mutation=Mutation,
schema_identifier=SchemaIdentifier(name="internal", version="1.4"),
) Here's the preview release card for twitter: Here's the tweet text:
|
Looks like I have some CI/CD failures. I'm happy to work on cleaning things up and fix them if the feature itself and the direction of the implementation make sense to the strawberry maintainers. |
@vdurmont this is an interesting proposal but we'd rather avoid adding new parameters to the (cc @erikwrede ) |
@jkimbo interesting suggestion. While an @strawberry.type(directives=[SupportsSchemas(schemas=("internal","external")), SchemaVersion(lte="1.4")]))
class User:
@strawberry.field(name="debugMessage",
directives=[SupportsSchemas(schemas=("internal",)), SchemaVersion(lte="1.4")])
def debugField():
pass However, that doesn't solve the problem of building the schema. That schema-building would still need to involve custom logic to pass the current schema version to the type extension (unless we figure out an API for On the topic of multiple schemas using the same types, I do have some concerns about the maintenance and clarity of the codebase. While I understand the use case for schema versioning, I worry that allowing multiple named schemas (e.g. "internal", "external"...) with the same types and fields could encourage design of more complex and harder to maintain code. Instead, it might be better to provide customers adjusted SDLs for each API and ensure that permissions are handled properly in the background (field visibility vs actually different schemas) or looking into federating the schema. That being said, I do recognize that there are situations where this feature might be necessary or appropriate. |
@erikwrede you could use the
Good point and IMO we should extend schema extensions to include hooks into the converter.
I completely agree with this and it's something to be mindful of @vdurmont . I do think we should provide enough functionality through extensions so that people can experiment with functionality like this to see how it might work in practice. |
@jkimbo Field Metadata is even better, I agree. Revisiting the post in Using these modifiers, internal and external schemas can be defined without building two separate schemas from the same types. This would reduce complexity by disabling the use of two fields with the same name that have different resolvers or return types. At the same time, we enable users to easily implement static and dynamic feature toggles, which is an important use case. The problem with these modifiers might be a deep integration into GQL-Core (to ensure introspection & validation are behaving correctly). Additionally, we'd need to ensure that they are clearly differentiating these from In any way, if we come up with a design for |
@erikwrede yes I like the idea of adding visibility filters but it would require some customisation of graphql-core. More discussion here: #361 |
Adds an optional `supported_schemas` feature to the fields. One can decide to limit the support for a particular field to: - A schema designated by name, - A schema, until a specific version, - A schema, from a specific version. When the GraphQL schema is being built, we may pass an identifier for the current schema name and version and the fields will be filtered appropriately. Examples of field definitions: ```python @strawberry.type class User: # [...] @strawberry.field(name="debugMessage", supported_schemas=[ SupportedSchema(name="internal"), ]) def get_debug_message(self) -> str: # This field will only appear in the schemas called `internal` # [...] @strawberry.field(name="riskScore", supported_schemas=[ SupportedSchema(name="internal", until_version="1.2"), ]) def get_old_risk_score(self) -> float: # This field will only appear in the schemas called `internal` that have # a version lower than or equal to `1.2` # [...] @strawberry.field(name="riskScore", supported_schemas=[ SupportedSchema(name="internal", from_version="1.3"), ]) def get_new_risk_score(self) -> float: # This field will only appear in the schemas called `internal` that have # a version higher than or equal to `1.3` # [...] ``` Examples of schema definition: ```python internal_schema = strawberry.Schema( query=Query, mutation=Mutation, schema_identifier=SchemaIdentifier(name="internal", version="1.4"), ) ```
7596c00
to
78c2473
Compare
CodSpeed Performance ReportMerging #2609 will not alter performanceComparing Summary
|
69df1ab
to
78c2473
Compare
for more information, see https://pre-commit.ci
Motivations
Running multiple version of the API
While GraphQL APIs are usually not versioned and should strive to never introduce
breaking changes, the fact is that you may want to version your GraphQL API and
run multiple versions in parallel within the same server.
Instead of duplicating code or creating a complex class hierarchy,
supported_schemas
enable you to annotate a field and specify in which version this field should be
available.
Running multiple schemas using "similar" objects
Sometimes, you may want to serve multiple GraphQL APIs that use the same objects
but with slightly different fields. A good example is that your third party GraphQL
API might expose the "core" fields of an object but your first party API might
add more information and more fields for your first party apps.
Instead of duplicating code or creating a complex class hierarchy,
supported_schemas
enable you to annotate a field and specify in which schemas those fields should
be available.
How it works
Adds an optional
supported_schemas
feature to the fields.One can decide to limit the support for a particular field to:
When the GraphQL schema is being built, we may pass an identifier for the current schema name and version and the fields will be filtered appropriately.
Examples of field definitions:
Examples of schema definition:
Checklist
I couldn't get the pre-commit hook to run but ran
black
manually: