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

Custom Input Scalars #35

Open wh-simon opened 6 years ago

wh-simon commented 6 years ago

The spec for GraphQL allow for custom input scalars. This lib should allow custom input scalar.

This would allow the server implementation of graphql to catch a input scalar like ProductId(unsigned) as a custom scalar and validate it in the graphql layer, insteed of just passing a int to backend.

jamesmacaulay commented 6 years ago

Somewhat accidentally, there is already a function that lets you define custom scalar variables for your query, but it's intended for enum types:

http://package.elm-lang.org/packages/jamesmacaulay/elm-graphql/1.8.0/GraphQL-Request-Builder-Arg#enum

You can use this for custom scalars just as well, providing the name of the custom scalar type from your schema as the first argument. The only limitation is that the value extraction function you provide must always return a String, which is then provided in your request's variables JSON as a string, so your GraphQL server must be able to parse the custom scalar type from string values.

I agree that this library should have a dedicated function for custom scalar variables, so I will probably simply add an alias to enum called customScalar with different documentation so that people can find it for this purpose.

I think the functions in GraphQL.Request.Builder.Arg serve just fine for non-variable arguments, since those don't declare their type explicitly in the query and so the server would not be able to perform any additional validation on them.

wh-simon commented 6 years ago

Yes, I found the enum but as you mention it puts limitations on what types the custom scalar can represent.. both in elm and server side. If the specification does't enforce this limitation, it kinda doesn't make sense to enforce them here?

Another solution would be implement the custom scalars for each basic type, prehaps a little bloaty:

customInt : String -> VariableSpec NonNull Int
customInt name =
    VariableSpec NonNull (TypeRef.namedType name) AST.IntValue

ect.

or adding it to the AST directly

type Value variableConstraint a
    = VariableValue variableConstraint String
    | IntValue Int
    | FloatValue Float
    | StringValue String
    | BooleanValue Bool
    | AnyValue (a -> Encode.Value) a
...

But it kinda makes some function definition look weird.

type alias ConstantValue a =
    Value Never a

type alias ArgumentValue =
    Value () Never

or

int : VariableSpec NonNull Int Never
int =
    VariableSpec NonNull TypeRef.int AST.IntValue

Any thought on that?

Meh, should have realized that the arg module can be used to the same end.