jamesmacaulay / elm-graphql

A GraphQL library for Elm
http://package.elm-lang.org/packages/jamesmacaulay/elm-graphql/latest
BSD 3-Clause "New" or "Revised" License
313 stars 19 forks source link

Provide a way to conditionally include a field of an input object variable #27

Closed jamesmacaulay closed 6 years ago

jamesmacaulay commented 6 years ago

Right now there's no way to specify a composite GraphQL variable that includes a field that might either be included or omitted conditionally...but there really should be!

The GraphQL.Request.Builder.Variable module currently exposes the nullable function that can be used to specify that a variable or some field within a variable might be sent as an explicit null. However, sending explicit null values is not a very common requirement – it is more common for nullable input object fields to simply be omitted from the JSON variables that accompany the GraphQL document in the request.

(Sometimes explicit null values are treated the same as an omission, sometimes they are treated differently, and unfortunately GraphQL schemas do not define this aspect of the server's behaviour.)

It wouldn't quite work to create another function like nullable for this purpose, because nullable operates on the level of the description of a lone value as opposed to a whole field. Instead we can introduce a new optionalField function that works much like the existing field function, with the difference that it would require the extractor function passed to it to return Maybe values:

optionalField
    :  String
    -> (objVariableSource -> Maybe fieldVariableSource)
    -> VariableSpec nullability fieldVariableSource
    -> Field objVariableSource

Here's an example usage:

type alias UpdateUserInputData =
    { id : String
    , name : Maybe String
    , email : Maybe String
    }

userDataVar : Variable { vars | userData : UpdateUserInputData }
userDataVar =
    required "userData"
        .userData
        (object "UpdateUserInput"
            [ field "id" .id id
            , optionalField "name" .name string
            , optionalField "email" .email string
            ]
        )

When either the name or email field is Nothing in the supplied UpdateUserInputData value, the corresponding field of the JSON variables would be omitted from the request.

akheron commented 6 years ago

How would this work with nullable fields? It should be possible to pass null or omit the field completely.

jamesmacaulay commented 6 years ago

@akheron thankfully this approach doesn’t inhibit explicit nulls. If you want to be able to pass them then you can use the nullable function as usual:

optionalNullableEmail : Field { a | email : Maybe (Maybe String) }
optionalNullableEmail =
    optionalField "email" .email (nullable string)

The email field is now double-Maybe-wrapped, where Nothing means omitting the field and (Just Nothing) sends an explicit null.

jamesmacaulay commented 6 years ago

Implemented optionalField in e3d0b8c2a1bda0a22a97db0490f89e4db155d2be, published in 1.7.0.