-
-
Notifications
You must be signed in to change notification settings - Fork 536
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
Support "void" functions #1648
Support "void" functions #1648
Conversation
Thanks for adding the Here's a preview of the changelog: Support "void" functions It is now possible to have a resolver that returns "None". Strawberry will automatically assign the new Exampe@strawberry.type
class Mutation:
@strawberry.field
def do_something(self, arg: int) -> None:
return results in this schema:
Here's the tweet text:
|
It might not conform to GraphQL's best practices, but sometimes we just don't need to return anything. This PR adds the scalar "Void" and uses it automatically when the type is `None` (or `NoneType`, which can be confusing) It mimics the `Void` type provided by [GraphQL Scalars](https://www.graphql-scalars.dev/docs/scalars/void)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uhm, I'm not sure we should add a void type, I personally never needed to do something like that (mostly because I use union types for returning errors).
Maybe it would be better do add some docs about this pattern? Have you seen something like this in other libraries?
/cc @jkimbo what do you think?
def from_maybe_optional( | ||
self, type_: Union[StrawberryType, type] | ||
) -> Union[GraphQLNullableType, GraphQLNonNull]: | ||
NoneType = type(None) | ||
if type_ is None or type_ is NoneType: | ||
return self.from_type(type_) | ||
elif isinstance(type_, StrawberryOptional): | ||
return self.from_type(type_.of_type) | ||
else: | ||
return GraphQLNonNull(self.from_type(type_)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this! /cc @BryceBeagle
Codecov Report
@@ Coverage Diff @@
## main #1648 +/- ##
==========================================
- Coverage 98.16% 98.14% -0.03%
==========================================
Files 129 129
Lines 4532 4528 -4
Branches 781 779 -2
==========================================
- Hits 4449 4444 -5
Misses 43 43
- Partials 40 41 +1 |
@patrick91 I think associating entities would be a valid use case for type Tag {
id: String!
slug: String!
}
type Post {
id: String!
tags: [Tag!]!
}
mutation tagPost(tagId: String!, postId: String!): Void |
I think in that case you might want the mutation to return the list of updated tags so that who uses your API (e.g Website) can have the updated list of tags after the mutation without having to re-fetch (e.g update the UI of the site) |
Yes, there is a consensus that this is not the best practice 😅 However I believe strawberry should still be able to handle it anyway
I can add a line about that in the "Mutations" section
TBH I'm quite new to GraphQL and I haven't seen much However GraphQL Scalars has this and it is used in many github projects |
Gotcha, maybe we should add support for it, but not bind it to If more people ask for it to be bound to What do you think? |
IMO this seems like a good default. You can override the behaviour of None with the scalar overrides anyway if someone doesn't like it. |
Hi 👋 You can find a preview of the docs here: https://strawberry.rocks/docs/pr/1648/types/scalars |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good to me! Thanks for the PR and thanks to everyone for sharing their opinions 😊
I've left a couple of minor comments, then this is ready to be merged :)
name="Void", | ||
serialize=_verify_void, | ||
parse_value=_verify_void, | ||
description="Represents NULL values", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
description="Represents NULL values", | |
description="Represents None values", |
maybe it is better to use None here? or Void? since it will be used in the schema :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think NULL
makes sense as it will be the returned value on the GraphQL protocol, while None
is a Python thing.
However I don't have strong feeling about that 🤷♂️
For whatever it is worth, I have defined this description to be the consistent with graphql-scalar's Void
Co-authored-by: Patrick Arminio <[email protected]>
Co-authored-by: Patrick Arminio <[email protected]>
Co-authored-by: Patrick Arminio <[email protected]>
else: | ||
argument_type = self.from_non_optional(argument.type) | ||
|
||
argument_type = cast(GraphQLInputType, self.from_maybe_optional(argument.type)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why, but the cast()
was necessary in a few places to make mypy happy 🤷♂️
name="Void", | ||
serialize=_verify_void, | ||
parse_value=_verify_void, | ||
description="Represents NULL values", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think NULL
makes sense as it will be the returned value on the GraphQL protocol, while None
is a Python thing.
However I don't have strong feeling about that 🤷♂️
For whatever it is worth, I have defined this description to be the consistent with graphql-scalar's Void
|
||
<Note> | ||
|
||
Mutations with void-result go against [GQL best practices](https://graphql-rules.com/rules/mutation-payload) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 The link is broken in the preview documentation.
Any idea what is wrong? Is there a different syntax inside <Note>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it needs the spaces, now it looks ok! https://strawberry.rocks/docs/pr/1648/general/mutations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
```grapqhl | ||
type Mutation { | ||
doSomething(arg: Int!): Void | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this file got messed up because of the suggestions I made 😊 can you fix it? I'm unable to push to your branch :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, good catch!
Thanks for contributing to Strawberry! 🎉 You've been invited to join You can also request a free sticker by filling this form: https://forms.gle/dmnfQUPoY5gZbVT67 And don't forget to join our discord server: https://strawberry.rocks/discord 🔥 |
Thanks! ❤️ |
Description
It might not be a best practice, but sometimes a mutation resolver just doesn't need to return anything.
Having a resolver that returns
None
is currently an error in strawberry.This PR adds the scalar "Void" and uses it automatically when the type is
None
(orNoneType
, which can be confusing 🙄)This scalar mimics the
Void
type provided by GraphQL ScalarsExample:
results in this schema:
Changes:
Void
scalar, and registered it to handleNone
andNoneType
None
type annotationsfrom_optional
andfrom_non_optional
intofrom_maybe_optional
GraphQLNonNull
Types of Changes
Checklist