Prohibit root mutation and subscription types from implementing interfaces#1225
Prohibit root mutation and subscription types from implementing interfaces#1225robrichard wants to merge 1 commit intomainfrom
Conversation
✅ Deploy Preview for graphql-spec-draft ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
9179c48 to
c7df6d3
Compare
benjie
left a comment
There was a problem hiding this comment.
This is a breaking change so warrants discussion. In particular, I'd love to know anyone whose schema this would actually break... who has interfaces on Mutation/Subscription? Why?
I'd also like to see text that says that the Mutation and Subscription root operation types may not be the named type of any field in the schema.
| service does not support mutations. If it is provided, it must be an Object type | ||
| that does not implement interfaces. |
There was a problem hiding this comment.
| service does not support mutations. If it is provided, it must be an Object type | |
| that does not implement interfaces. | |
| service does not support mutations. If it is provided, it must be an Object type | |
| that does not implement any interface and is not a member of any union. |
There was a problem hiding this comment.
If we were to also restrict the mutation and subscription root type from being a member of a union, I think that validation should go here instead?
Since it would make the union type invalid, not the root type?
There was a problem hiding this comment.
I see what you’re saying, but a union won’t know whether something is a root operation type or not. Nothing knows that until the schema itself is constructed, I think?
There was a problem hiding this comment.
I think the whole schema has to be constructed in either case? At least in GraphQL-JS we know what the root types are when validating union members.
is not a member of any union
To translate this to code we'd have to iterate through each type, find all unions and then check each member to see if it's the same as the root type.
Adding it to the union validation we just need to get the root subscription and mutation types from the schema and check against each member.
| Similarly, the {`subscription`} _root operation type_ is also optional; if it is | ||
| not provided, the service does not support subscriptions. If it is provided, it | ||
| must be an Object type. | ||
| must be an Object type that does not implement interfaces. |
There was a problem hiding this comment.
| must be an Object type that does not implement interfaces. | |
| must be an Object type that does not implement any interface and is not a | |
| member of any union. |
|
Discussed at the Primary May 2026 WG, consensus was that this could potentially block a future implementation of nested serially executed fields or mutation fields nested under namespaces. I will keep this PR open for a bit to gather any other feedback but will be proceeding with updating the defer/stream validation rules to avoid this issue. |
I ran into this issue working on validation rules for
@deferand@stream: We would like to prevent@deferand@streamfrom being used on root level mutation or subscription fields, since these fields have special meaning.One approach for a validation rule for this would be to do an algorithm like CollectFields to gather all the root level fields by traversing through fragment spreads. This is what's currently done in the (Subscription Operation Definitions - Single Root Field)[https://spec.graphql.org/September2025/#sec-Single-Root-Field] rule.
An easier approach is to just check the parent type and see if that type is the root subscription or mutation field, which is what I opted for.
That validation rule looks something like this:
But this could be bypassed if these root fields implement an interface. Example:
This is a breaking change, but i'd like to see if there are any use cases for this or if we think it's used by the community. If not this could simplify defer/stream and possibly future validation rules, otherwise I can switch to the
collectFieldsapproach.Further question - should we also expand this validation to union types? It's not required for this validation use case but might also be nice to have.
GraphQL-JS PR: graphql/graphql-js#4669