-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
[RFC] Make root query operation type optional #631
base: main
Are you sure you want to change the base?
Conversation
Currently the spec states:
We can confirm that this is the case in type Query {
query: Query
} and running a query such as: {
query {
query {
__schema {
queryType {
name
}
}
}
}
} So schema introspection is linked to the the root query type. If we want to separate schema introspection ( |
I agree with @benjie simplest solution would be to just always add |
This makes the root query operation type optional like the other types. Schema introspection is unaffected. The `__schema` and `__type` fields were already described as "implicit" and not part of the defined root query operation type. A schema without a root query type therefore only supports these queries. Fixes graphql#490
ac823c9
to
5e643f2
Compare
Thanks! I've updated the proposed changes in response to your feedback. I wasn't initially keen on generating a stub Query type if one isn't defined, but agree that this is more backwards compatible.
|
I'm sad to say I've left this hanging for too long. Having thought a bit more about it, I think I'm actually pivoting back to allowing an empty root Query type to support this case.
TL;DR: I think it would be a smaller change to allow defining an empty root Query type, than creating one if not already defined. I think there's a strong case for loosening the empty object type restriction, in general, to support placeholder types during development, primarily. The use of "magical" obviously reflects a value judgment on my part. 1 Introspection queries aren't always allowed (e.g. https://github.com/helfer/graphql-disable-introspection/blob/master/index.js), but the schema still has the types. |
@victorandree Completely agree. I think we should allow for empty input and output objects in general to allow for easier schema evolution. For example, in my GraphQL APIs we follow the Relay Mutation Input Object Specification (read why here); but sometimes a mutation doesn't require any inputs at first and doesn't have any outputs yet; e.g.: # IDEAL SCENARIO - not currently valid GraphQL
input MyMutationInput {}
type MyMutationPayload {}
type Mutation {
myMutation(input: MyMutationInput!): MyMutationPayload
} But later we may wish to evolve that without having to deprecate the old mutation: input MyMutationInput {
size: Int = 1
}
type MyMutationPayload {
numberAffected: Int
}
type Mutation {
myMutation(input: MyMutationInput!): MyMutationPayload
} Being able to evolve an API in this way (and maintain consistency during design) is highly desirable. This potentially also encourages allowing empty selection sets, but for now you can solve this with # This mutation would be valid against both GraphQL schemas above.
mutation MyMutation($input: MyMutationInput = {}) {
myMutation(input: $input) {
__typename
}
} Since we don't currently have empty types, we either have to add a stub field to both input and payload: # clientMutationId is a field required by old Relay Legacy, but has
# been phased out. However it solves this problem well because it is
# available on both input and output, making our types non-empty.
input MyMutationInput {
clientMutationId: String
}
type MyMutationPayload {
clientMutationId: String
}
type Mutation {
myMutation(input: MyMutationInput!): MyMutationPayload
} or have a versioned/alternative field, and have the original field break the schema mutation conventions: input MyMutationV2Input {
size: Int = 1
}
type MyMutationV2Payload {
numberAffected: Int
}
type Mutation {
# Original mutation, doesn't adhere to same conventions as other mutations :(
myMutation: Boolean
@deprecated(reason: "Use `myMutationV2` instead")
# Replacement mutation, now supports input and payload
myMutationV2(input: MyMutationV2Input!): MyMutationV2Payload
} Neither of which are particularly desirable. |
@victorandree I think an RFC is the right way to go. CC me when you raise it, I'm happy to help editing/etc. I'm not sure I have the resources currently to champion another thing though! |
Hi, is there any update on this? |
@huehnerlady I regret I haven't really touched this since my comment last April. It wasn't meant as an April Fool's, but...
I'm afraid I don't have the bandwidth to champion this, though, otherwise I would have. Unless someone else believes in the approach outlined here, this PR should probably be closed. The other issues referenced in this PR describe prior art. |
@victorandree thank you for your wuick response. is there any other issue where the empty type definition is discussed? One of the 2 discussions was closed ant the other seemed to have a different focus (on actual query objects). Many thanks |
(Just a quick note to say that I'm still particularly interested in this topic, maybe not this particular route (you can see my opinions above) but in solving this issue. However I've got too many other spec changes on my plate right now to champion anything else 😬 Definitely interested in revisiting it in the summer if you've not had time to do so yourself by then @victorandree. i.e. don't close it yet 😉 ) |
e5d241d
to
6c81ed8
Compare
Hi there. Do you have any opportunity to revisit this? Maybe the lib/framework authors |
This change would make the root
query
operation type optional, consistent withmutation
andsubscription
. In this proposal, schema introspection would still work as before, by allowing a "default"query
type if one is not defined, exposing only the implicit fields__schema
and__types
(in response to PR feedback).The change is worded so that a "schema must define at least one root operation type.". See below for motivation and alternatives.
See #490 for background on this issue.
Motivation
Not all GraphQL APIs need a
query
interface but do fine with justmutation
orsubscription
. This becomes especially apparent for micro services. Such services are forced to expose dummy query fields, with real world examples such as_
,version
,helloWorld
, ordontQueryMe
.Alternative: Allow root query operation type to be empty
Instead of making the root
query
operation type optional, it can be allowed to not have any fields.This has already been proposed (see #606), but would have wider impacts than just making
query
optional. A benefit of this approach is that schema introspection wouldn't need any new treatment.Allowing for only the query root, or others, to be empty would require validating that type in the context of how it's used, which seems strange to me (e.g. if a type is only allowed to be empty if it's called
Query
or is used inschema { query: Query }
).Alternative: Don't require any root type
A schema which only defines types without any operations could be useful in some scenarios, where a schema in service A is perhaps stitched or used as the basis for another. To support this, one could drop the requirement "A schema must define at least one root operation type."
I think this would be esoteric and counter to existing requirements enforcing a "useful" schema (for example requiring composite types not to be empty).