Skip to content
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

Proposal: Do not require query as a root operation type #490

Open
jonbudd opened this issue Aug 2, 2018 · 8 comments
Open

Proposal: Do not require query as a root operation type #490

jonbudd opened this issue Aug 2, 2018 · 8 comments
Labels
👻 Needs Champion RFC Needs a champion to progress (See CONTRIBUTING.md) 💭 Strawman (RFC 0) RFC Stage 0 (See CONTRIBUTING.md)

Comments

@jonbudd
Copy link

jonbudd commented Aug 2, 2018

The official spec states:

A schema defines the initial root operation type for each kind of operation it supports: query, mutation, and subscription; this determines the place in the type system where those operations begin.
The query root operation type must be provided and must be an Object type.
The mutation root operation type is optional; if it is not provided, the service does not support mutations. If it is provided, it must be an Object type.
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.

Why must the query root operation be provided? It seems to me that it should be perfectly legal to have an API that only contains a mutation or subscription. Especially with the advent of subscriptions, which are similar to queries in that they're only meant for retrieving data rather than modifying it, I'm not sure I see a purpose in requiring a query specifically.

Some more details on my specific use case, to provide some more context: I'm employing schema stitching to merge numerous disjoint schemas together and present them as one. I have a subscription that I want to host in its own API, since doesn't logically belong in any of my other APIs; but the GraphQL implementation I'm using expects that a query will exist in the schema, forcing me to add an unnecessary query into my schema. Removing this stipulation from the spec (as well as from the implementation) would allow for this use case.

This stipulation is mentioned in #218, but in a slightly different context, and I think it warrants further discussion.

@IvanGoncharov
Copy link
Member

@jonbudd Without query root type you can't execute introspection query.
How should we solve this problem?

@grantwwu
Copy link
Contributor

grantwwu commented Aug 2, 2018

@IvanGoncharov I'm not an expert here, but surely we can just amend the spec - allowing implementations to generate an empty one that only contains the introspection queries? - to allow introspection queries when there's no query root type.

@jonbudd
Copy link
Author

jonbudd commented Aug 3, 2018

Hmm, I do see your point, @IvanGoncharov. But I don't think that "The query type must exist at runtime" and "The schema author needs to explicitly mention the query type" are quite the same thing, which is what I think @grantwwu is getting at. query already has some other implicit logic:

  • Query is assumed to be the name of the query root type when left unspecified
  • __type and __schema fields are implicit fields in the query root type

I think it logically follows that a schema author using the type schema definition language shouldn't have to explicitly define the query root type; it should assume that Query is the root type if nothing is present, and then the introspection fields should be applied to that Query type. This set of rules would allow the query type and its introspection fields to still be present at runtime, even when no query root type is defined. This would even allow for "empty" schemas, which only contain introspection queries.

If this idea is displeasing, I think another solution would be to allow authors to declare an empty query type, like is suggested in #218, ie:

type Query {
}
...

The problem, though, is that this isn't allowed either according to the spec; Section 3.6 of the spec states that 1. An Object type must define one or more fields.

Since a query root type must be an Object type, it can't be empty, making the definition above invalid. That is, unless the implicit fields __type and __schema count toward its fields. Is this the case? If so, I would say that this should be explicitly stated in the spec so that it's clear that an empty query type is valid according to the type system definition language, since introspection fields are added at runtime. This would ensure that implementation authors don't fall into a trap of thinking that it's not allowed.

Meanwhile, if these fields do not count toward the list of fields under the query type, then we're back at square one, with my use case still being impossible. There are a couple of solutions I can think of to this:

  1. Allow Object types to be empty. I'm not sure I understand the reasoning behind forcing them to be nonempty anyway, except to avoid bloat.
  1. Change it so that __type and __schema are counted as fields in the Query object by default. This would make it always nonempty, even when it "looks" empty according to the type definition schema language. This makes query somewhat of a special case, but it's already a special case due to the introspection fields being added on any query root type.

@stubailo
Copy link
Contributor

stubailo commented Aug 3, 2018

Yeah honestly this could just be a feature request for graphql-js and not a thing for the spec. Perhaps buildSchema('') should be valid, and return a schema that only replies to introspection.

@IvanGoncharov
Copy link
Member

I think it logically follows that a schema author using the type schema definition language shouldn't have to explicitly define the query root type;

Yes, you're right but problem is that this behavior is SDL specific. So schemas defined not as SDL can have Query type that does not query root type so we would be forced to add __IntrospectionOnlyQuery or similarly named type.

And more importantly __type, __typename, __schema are implicit fields so any such type will be empty.
So I think to allow the explicitly empty type is the best option we have.

This makes query somewhat of a special case, but it's already a special case due to the introspection fields being added on any query root type.

It's not since __typename added to every type, including query root type.

Allow Object types to be empty. I'm not sure I understand the reasoning behind forcing them to be nonempty anyway, except to avoid bloat.

AFAIK, the idea was to detect developer mistake and to limit the number of corner cases. For example, all GraphQL documentation solutions should add a special check to skip Fields title otherwise it will look strange without a list of fields underneath.
I'm also not sure if all programming languages support empty types and this is important for code generation.

I think it's can be possible to remove this rule but it deserves PR and a proper discussion in it.

Anyone wants to be a champion for this proposal and create a PR?

@leebyron leebyron added 👻 Needs Champion RFC Needs a champion to progress (See CONTRIBUTING.md) 💭 Strawman (RFC 0) RFC Stage 0 (See CONTRIBUTING.md) labels Oct 2, 2018
victorandree added a commit to victorandree/graphql-spec that referenced this issue Aug 5, 2019
Supporting empty types, e.g. objects, input objects and interfaces without any fields, has concrete use cases.

- Support for GraphQL APIs without any `Query` operation type fields, for example, if the API only support mutations or subscriptions (see graphql#490). This allows defining an empty `Query` object type, while still supporting introspection.
- Support for algebraic data types (see graphql#568), where `__typename` is the only relevant field
- Potentially to support "well-known" (but empty) extensions of the introspection schema (see graphql#300)

This is a minimalist spec change, which simply removes the relevant items under the type validation sub sections.
It would probably be helpful to motivate the change and mention that one or more fields _should_ be present for a (typically) useful schema.

The requirement for composite types (object, input objects and interfaces) to define "one or more fields" was introduced in 0599414.
This change references graphql/graphql-js#368, motivating the change with:

> Since GraphQL always requires you to select fields down to scalar values, an Object type without any defined fields cannot be accessed in any way in a query.
> This could be even more problematic for Input Objects where a required input object argument with no fields could result in a field that is impossible to query without producing an error.

With regards to these objections:

- It's always possible to select `__typename` and therefore even empty object types can be useful (as e.g. algebraic data types)
- Passing an empty input object appears syntactically valid:

    ```gql
    mutation {
      update(input: {}) {
        name
      }
    }
    ```

I think this proposal fulfills the guiding principles by enabling new capabilities motivated by real use cases.
This change does not make any previously valid schema invalid, so largely preseves backwards compatibility.

Fixes graphql#568 and graphql#490 at the specification level.
victorandree added a commit to victorandree/graphql-spec that referenced this issue Aug 28, 2019
Supporting empty types, e.g. objects, input objects and interfaces without any fields, has concrete use cases.

- Support for GraphQL APIs without any `Query` operation type fields, for example, if the API only support mutations or subscriptions (see graphql#490). This allows defining an empty `Query` object type, while still supporting introspection.
- Support for algebraic data types (see graphql#568), where `__typename` is the only relevant field
- Potentially to support "well-known" (but empty) extensions of the introspection schema (see graphql#300)

This is a minimalist spec change, which simply removes the relevant items under the type validation sub sections.
It would probably be helpful to motivate the change and mention that one or more fields _should_ be present for a (typically) useful schema.

The requirement for composite types (object, input objects and interfaces) to define "one or more fields" was introduced in 0599414.
This change references graphql/graphql-js#368, motivating the change with:

> Since GraphQL always requires you to select fields down to scalar values, an Object type without any defined fields cannot be accessed in any way in a query.
> This could be even more problematic for Input Objects where a required input object argument with no fields could result in a field that is impossible to query without producing an error.

With regards to these objections:

- It's always possible to select `__typename` and therefore even empty object types can be useful (as e.g. algebraic data types)
- Passing an empty input object appears syntactically valid:

    ```gql
    mutation {
      update(input: {}) {
        name
      }
    }
    ```

I think this proposal fulfills the guiding principles by enabling new capabilities motivated by real use cases.
This change does not make any previously valid schema invalid, so largely preseves backwards compatibility.

Fixes graphql#568 and graphql#490 at the specification level.
victorandree added a commit to victorandree/graphql-spec that referenced this issue Oct 11, 2019
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
victorandree added a commit to victorandree/graphql-spec that referenced this issue Oct 23, 2019
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
victorandree added a commit to victorandree/graphql-spec that referenced this issue Oct 25, 2019
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
dariuszkuc added a commit to dariuszkuc/graphql-kotlin that referenced this issue Jan 9, 2020
GraphQL specification requires Query type to be present as it is required to run introspection query. Per specification it also shouldn't be possible to generate empty complex types (objects, input objects or interfaces) and they should expose at least a single field. Since root Query type is a special GraphQLObjectType it also has to expose at least a single field.

Breaking changes:
* at least single Query is required when generating the schema
* split `didGenerateQueryType` hook (and corresponding `Mutation` and `Subscription` hooks) into `didGenerateQueryFieldType` (previous functionality) and `didGenerateQueryObjectType` hooks to allow more granular control when generating the schema
* default `SchemaGeneratorHooks` now performs validation of generated object types (including special query, mutation and subscription types), input object types and interfaces to ensure we don't generate empty complex object types

see: graphql/graphql-spec#490 and graphql/graphql-spec#568
dariuszkuc added a commit to ExpediaGroup/graphql-kotlin that referenced this issue Jan 10, 2020
)

* BREAKING CHANGE: empty complex types should fail schema generation

GraphQL specification requires Query type to be present as it is required to run introspection query. Per specification it also shouldn't be possible to generate empty complex types (objects, input objects or interfaces) and they should expose at least a single field. Since root Query type is a special GraphQLObjectType it also has to expose at least a single field.

Breaking changes:
* at least single Query is required when generating the schema
* split `didGenerateQueryType` hook (and corresponding `Mutation` and `Subscription` hooks) into `didGenerateQueryFieldType` (previous functionality) and `didGenerateQueryObjectType` hooks to allow more granular control when generating the schema
* default `SchemaGeneratorHooks` now performs validation of generated object types (including special query, mutation and subscription types), input object types and interfaces to ensure we don't generate empty complex object types

see: graphql/graphql-spec#490 and graphql/graphql-spec#568
@felinto-dev
Copy link

felinto-dev commented Mar 20, 2021

I have the same problem here. I agree that GraphQL should not work if you don't have at least one query configured, but I don't think should throw an exception.

@huehnerlady
Copy link

I think A Mutation is also a query, so in my opinion it should be possible to just have a mutation configured, hence to havye any type of query but not just the Queryitself

@felinto-dev
Copy link

I ended up solving my problem as follows:

  • I created a query called "getHello" that just returns "Hello" and if the user sends his name as a parameter it will return "Hello" + the user's name.

Maybe it is a possibility of having no query configured, instead of returning an EXCEPTION, GraphQL returns a simple "getHello" query that certifies that the GraphQL server is working?

dariuszkuc added a commit to dariuszkuc/graphql-kotlin that referenced this issue Aug 5, 2022
…xpediaGroup#541)

* BREAKING CHANGE: empty complex types should fail schema generation

GraphQL specification requires Query type to be present as it is required to run introspection query. Per specification it also shouldn't be possible to generate empty complex types (objects, input objects or interfaces) and they should expose at least a single field. Since root Query type is a special GraphQLObjectType it also has to expose at least a single field.

Breaking changes:
* at least single Query is required when generating the schema
* split `didGenerateQueryType` hook (and corresponding `Mutation` and `Subscription` hooks) into `didGenerateQueryFieldType` (previous functionality) and `didGenerateQueryObjectType` hooks to allow more granular control when generating the schema
* default `SchemaGeneratorHooks` now performs validation of generated object types (including special query, mutation and subscription types), input object types and interfaces to ensure we don't generate empty complex object types

see: graphql/graphql-spec#490 and graphql/graphql-spec#568
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
👻 Needs Champion RFC Needs a champion to progress (See CONTRIBUTING.md) 💭 Strawman (RFC 0) RFC Stage 0 (See CONTRIBUTING.md)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants