Description
Preface
it is a common practice in strawberry that when your data layer have
an optional field i.e
class Person:
name: str
phone: str | None = None
and you want to update it you would use UNSET
in the mutation input
in order to check whether this field provided by the client or not like so:
@strawberry.input
class UpdatePersonInput:
id: strawberry.ID
name: str| None
phone: str | None = UNSET
@strawberry.mutation
def update_person(input: UpdatePersonInput) -> Person:
inst = service.get_person(input.id)
if name := input.name:
inst.name = name
if input.phone is not UNSET:
inst.phone = input.phone # ❌ not type safe
service.save(inst)
Note that this is not an optimization rather a business requirement.
if the user wants to nullify the phone it won't be possible other wise
OTOH you might nullify the phone unintentionally.
This approach can cause lots of bugs since you need to remember that you have
used UNSET
and to handle this correspondingly.
Since strawberry claims to
Strawberry leverages Python type hints to provide a great developer experience while creating GraphQL Libraries.
it is only natural for us to provide a typesafe way to mitigate this.
Proposal
The Option
type.which will require only this minimal implementation
import dataclasses
@dataclasses.dataclass
class Some[T]:
value: T
def some(self) -> Some[T | None] | None:
return self
@dataclasses.dataclass
class Nothing[T]:
def some(self) -> Some[T | None] | None:
return None
Maybe[T] = Some[T] | Nothing[T]
and this is how you'd use it
@strawberry.input
class UpdatePersonInput:
id: strawberry.ID
name: str| None
phone: Maybe[str | None]
@strawberry.mutation
def update_person(input: UpdatePersonInput) -> Person:
inst = service.get_person(input.id)
if name := input.name:
inst.name = name
if phone := input.phone.some():
inst.phone = phone.value # ✅ type safe
service.save(inst)
Currently if you want to know if a field was provided
Backward compat
UNSET
can remain as is for existing codebases.
Option
would be handled separately.
which Option
library should we use?
- Don't use any library craft something minimal our own as suggested above.
- ** use something existing**
The sad truth is that there are no well-maintained libs in the ecosystem.
Never the less it is not hard to maintain something just for strawberry since the implementation
is rather straight forward and not much features are needed. we can fork either
and just forget about it.
- allow users to decide
# before anything
strawberry.register_option_type((MyOptionType, NOTHING))
then strawberry could use that and you could use whatever you want.
- Core functionality
- Alteration (enhancement/optimization) of existing feature(s)
- New behavior