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

Fields that do not return a value #906

Open
spawnia opened this issue Dec 1, 2021 · 7 comments
Open

Fields that do not return a value #906

spawnia opened this issue Dec 1, 2021 · 7 comments

Comments

@spawnia
Copy link
Member

spawnia commented Dec 1, 2021

Motivation

Our application defines some mutations that are purely used to trigger side effects and don't return a value.

GraphQL enforces the definition of a return type for those fields. Due to this constraint and lack of a better alternative, I have found all of the following permutations to be present in our codebase:

  • type Boolean!, resolver always returns true
  • type Boolean!, resolver returns a boolean indicating success - swallowing errors!
  • type Boolean, resolver is defined to return void, result is serialized to null

While this inconsistency is not pretty for someone on the server side, it is especially bad for the client. Depending on the quality of the schema documentation, they won't know for sure if the return type is meaningful.

Idea

Introduce an idiomatic way to represent the notion of a field that returns no value.

Possible Solutions

No Type

Allow defining the field with no return type at all.

type Mutation {
  fireAndForget
}
  • terse definition
  • affects the grammar of GraphQL, perhaps causes ambiguities (?)
  • requires making __Field.type nullable in introspection

It is unclear to me how this field would be represented in the serialized result, given it is a map and there has to be something in the place of the value. Perhaps null?

void

Introduce a new keyword void that acts as a pseudo-type.

type Mutation {
  fireAndForget: void
}
  • reserving a keyword might cause breakage
  • more explicit than omission

Unit Type

Introduce a new type Unit that allows only one possible value and thus can hold no information.

type Mutation {
  fireAndForget: Unit
}

Some rules:

  • Unit is implicitly non-nullable, otherwise the value could be null or whatever the representation of Unit is
  • Unit can not be used in lists

Building upon existing types, I can see the definition of this type being either one of:

  • a scalar with only a single allowed value - the string UNIT
  • an enum with only a single value UNIT

I can see this type being useful as an argument too, enabling a binary toggle between not passing the argument at all or passing it.

@rivantsov
Copy link
Contributor

+1 for void

@gregory-claeyssens
Copy link

I have had similar problems, and also went with a boolean response as a workaround. This seems like a valid addition to me.
From the 3 suggestions here, @void seems to be the only one I can't find any downsides with. It does what you would expect and is hard to misinterprit.

@benjie
Copy link
Member

benjie commented Dec 8, 2021

Would the field return null or would it have no representation in the payload? What would happen if the field threw an error?

@rivantsov
Copy link
Contributor

I think it should be 'no repr in payload', just like with 'skip(if:true)'; if error - just same stuff, error object should appear in errors, path should include the field name at the end, don't see any problem with that

@spawnia
Copy link
Member Author

spawnia commented Dec 12, 2021

I really like the idea of having no representation in the payload, and it is great to see that there is already an existing construct that does the same.

This seems good for schema evolution: with the field not being present at all, clients will very likely have no code that somehow depends on the result (which might be the case with any pseudo-value). Then, it is easier to introduce any return type once the field has evolved to actually provide useful information.

What would be the best representation in introspection? A very simple solution would be for the field to have no return type at all, thus letting __Field.type be null. I wonder if that causes problems for consumers of introspection, such as GraphiQL or various code generation tools. Perhaps they would be better served if the field returned a concrete scalar/enum type.

@rivantsov
Copy link
Contributor

@spawnia, one option would be to do like c#:
Void is an actual type in type system, 'void' is a keyword, equivalent of Void; (same as String type and string keyword).
We can just have Void as an actual type (scalar kind of). We do not need 'void' as keyword, intro would return 'Void' as type

@benjie
Copy link
Member

benjie commented Dec 13, 2021

@spawnia The selection set validation rules would have to be modified to allow changing from void with no selection set to an object payload type with selection set. One option is to use an “empty” object type, but this comes with a bunch of caveats:

  • we don’t support empty object types yet (but it has been discussed a number of times)
  • We don’t support empty selection sets
  • If it allows __typename you need to ensure it has the same name as your ultimate payload type
  • if you don’t allow __typename that’ll confuse a load of clients; also we don’t support empty selection sets

I don’t see a clear path for this kind of schema evolution. 😔

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants