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

Type-safe "optional-nullable" fields #3779

Open
1 of 3 tasks
nrbnlulu opened this issue Feb 12, 2025 · 2 comments
Open
1 of 3 tasks

Type-safe "optional-nullable" fields #3779

nrbnlulu opened this issue Feb 12, 2025 · 2 comments

Comments

@nrbnlulu
Copy link
Member

nrbnlulu commented Feb 12, 2025

Preface

it is a common practice in strawberry that when you'r data layer have
an optional field i.e

class Person:
    name: str
    phone: str | None = None

and you want to update it you'd use UNSET in the mutation
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.

@strawberry.input
class UpdatePersonInput:
    id: strawberry.ID
    name: str| None
    phone: Option[str | None] = NOTHING

@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  # ✅  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?

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.

Another option would be

# 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
@fruitymedley
Copy link

To clarify, is the following the use case you're interested in?

mutation Update {
    updatePerson(  ## this operation sets phone to null
        input: {
            id: ..., 
            name: ...,
            phone: null
        }
    ) {
        ...
    }
}

vs

mutation Update {
    updatePerson(  ## this operation does not modify phone number
        input: {
            id: ..., 
            name: ...
        }
    ) {
        ...
    }
}

@nrbnlulu
Copy link
Member Author

yep

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

2 participants