juniper
juniper copied to clipboard
Native scalar support for json (w/ feature = "json")
I would like to be able to use a serde_json::Value as an Input or Output value in the graph.
This can work fine for output values, but doesn't work for Input values.
Simple-ish work arounds include newtype-ing json::Value and having it serialize as some sort of string or blob--- but that causes easily avoidable and unnecessary pain for both a Javascript Client and for the server itself (which might, for example use the same struct with both diesel and juniper).
This doesn't work (I think) because using an arbitrary JSON value as an input (or output) would require unusual handling during validation that is not described by the GraphQL spec. We could, as an extension have special handling for a serde_json::Value that does not attempt to further validate the input after identifying that the input variable / input-object field is expecting arbitrary JSON.
I believe an extension like this must be implemented within juniper itself (although if I'm wrong, I'd love to know). This should be fairly low risk to add to core, as serde_json is an incredibly widely used and well supported rust crate. Presumably it would be a off-by-default feature flag since it is an extension to the GraphQL spec.
Did you see https://github.com/graphql-rust/juniper/commit/2e5df9f8a469943d694a1deb67910cbbdcd6cae4 on master?
@LegNeato --- that doesn't work for actually allowing a json::Value to serialize as you'd expect (e.g. as an Object rather than a blob); so it seems, at least in part because the ScalarValue trait doesn't have a way to represent complex objects.
There are no ScalarValue::as_array or ScalarValue::as_object equivalent; ParseScalarValue can't parse from an object or array (there is no ScalarToken for arrays or objects).
Presumably an alternative to providing json_scalar as a feature would be to provide a complex_scalars feature that adds arrays and objects to the scalar parsing infrastructure. While not everyone will want raw JSON scalars, the serde_json crate is more widely depended-on than chrono, uuid, and url.
Side Note: AFAIK the problem can't be fully solved by implementing GraphQLType normally for serde_json::Value because there isn't a way to allow such a type to be used as both an input and output value.
Related discussion on this issue: https://github.com/facebook/graphql/issues/101
Is there a workaround for this or could someone point me in the right direction in the codebase so I could submit a PR? The only place I can see is that ScalarValue trait...
I'm not sure we want to do this? I don't see a massive benefit over just creating custom scalars and de/serializing Value to a string on both ends. Sure, in the schema it will ultimately be a String once you follow the scalar type def chain and clients will have to have logic, but I am not sure this is worth deviating from the spec. The comments in the above graphql issue basically point to it not being a clear win in general.
I am sure I am missing something though...
In any case, I put up a PR for the current-style integration. It still serializes and unserializes to a GraphQL String though, which from the comments above sounds like is not sufficient.
I guess it doesn't conform to the spec, but in another Node.js (Apollo) server I have worked with, I used something like https://github.com/taion/graphql-type-json so that you could specify JSON a lot more ergonomically via dev tools like graphiql. It is not a huge deal though, I went ahead and made my own scalar that wraps a Value via a newtype struct.
Being able to directly use Value would be a lot better though, so thanks for the PR!
@LegNeato - yes, for me the benefit would be if serialization did not serialize to string; while having a standard serialized-to-string Json is useful in terms of "batteries included" for library users, I think the benefit is to minimize complexity of deserialization/serialization in clients by having it be "full" JSON (instead of serialized json within json).
Any update on this?