juniper icon indicating copy to clipboard operation
juniper copied to clipboard

Determine if Juniper should support alternate representations

Open LegNeato opened this issue 5 years ago • 9 comments

Currently Juniper has no concept of the GraphQL Schema Language / SDL (referred to as "SDL" from now on). There is no way in Juniper to go from a schema defined in Rust to the SDL representation of it and vice versa.

Some 3rd party GraphQL tools work with the SDL and thus would not work with Juniper. More often than not, many of those tools support introspection query's output (usually in a file called schema.json) as well. Juniper "supports" the introspection query, as it is a standard GraphQL query that any client could do, though we have a task to make it more ergonomic.

The reference GraphQL server Graphql.js includes support for:

  • Doing everything in JS
  • Defining in JS and converting to SDL
  • Defining in SDL and converting to JS
  • Built in introspection query
  • Introspection -> JS

In fact, they explicitly test that you can go around between all these representations and have the same result.

In the Rust world we have:

  • Doing everything in Rust (juniper)
  • Defining in Rust and converting to SDL (https://github.com/graphql-rust/juniper/pull/324)
  • Defining in SDL and converting to Rust (juniper-from-schema)
  • Built in introspection query (https://github.com/graphql-rust/juniper/issues/307)
  • Introspection -> Rust (no task or project, though graphql-client does similar but doesn't use Juniper's schema representation)

So we need to decide if we follow GraphQL.js and include all the functionality in Juniper or we say Juniper is the "Rust-only" world above and everything else is handled in other crates (either in this workspace or in their own projects). I'm also not even sure we can include everything, as we need to do some work at compile time rather than runtime like GraphQL.js does.

/cc @theduke @mhallin @tailhook @davidpdrsn @tomhoule

(originally discussed in https://github.com/graphql-rust/juniper/pull/324)

LegNeato avatar Mar 05 '19 20:03 LegNeato

From #324

Is it a view of the schema one would reasonably expect juniper to handle? Or is it a nice-to-have that isn't intrinsic to how juniper does things.

I would say this is something juniper should handle. Having an actual schema file that describes your API is one of the key things that intrigues me about GraphQL. It also feels like the "Rusty" approach because it puts greater emphasis on type safety, something I value a lot.

At the place where I work we're soon going to be experimenting with replacing parts of our Ruby on Rails API with Rust + GraphQL. For us being able to go from SDL to Rust would fit our workflow well I think. When the backend and mobile teams meet to decide the spec for some new feature we are going to build, we would add new fields/types to our schema. The mobile devs can then start using the schema right away to build queries in whatever way their language and tools allows for. The backend team can take the schema, "convert" it to Rust and fill in the blanks. While I don't have lots of experience with this workflow (yet) I think it is going to work well.

Having the whole schema available for juniper would also allows you to make things like type safe look aheads. Not sure this would be possible if the schema wasn't known at compile time.

davidpdrsn avatar Mar 07 '19 10:03 davidpdrsn

Why not do the opposite (define in rust and have the schema file be a view into what rust has defined)? That's what I am doing, as you get nice things like autocomplete in your editor without having to teach it SDL, easy grepability, etc. Plus, you can add lint rules for your graphql logic without having to add a graphql-specific framework. I try to stay in rust as much as possible. I use rust as the source of truth and see the SDL as serialization format that graphql tools outside of rust can use.

I understand things may be different for greenfield development like I am currently doing.

LegNeato avatar Mar 07 '19 17:03 LegNeato

I love the idea of QueryTrails btw

LegNeato avatar Mar 07 '19 17:03 LegNeato

After some thought, I do think it makes sense to be inclusive here and support all the possibilities outlined by LegNeato.

While I personally I am not a big fan of the Schema -> Rust workflow, I think many find it easier to work with. Also, as @davidpdrsn mentioned, sometimes you have the schema as the source of truth and implement both clients and servers based on that schema, or a subset of a schema in case of nested/forwarding servers. The other attempt at a GraphQL server also used this workflow (https://github.com/nrc/graphql).

I think it makes sense to include the juniper_from_schema functionality into the main crate. (With some adaptations. I'm not a fan of their use of #[xx] Rust style annotations in the schema, since that breaks other tools. )

A general AST with conversion functionality would also make the implementation of graphql-client much easier I think.

theduke avatar Mar 07 '19 18:03 theduke

From the perspective of graphql_client, ingesting the GraphQL AST or the introspected schema in JSON form is about 100 lines each, so adding new sources is not a problem. The only obstacle to doing so now is that the schema and query representation juniper uses are private (this may have changed since I checked).

The internal representations (what the ASTs are converted to) for schema and query in graphql_client at the moment are just what it needs, so I am not sure they should be replaced, but I could be missing important reasons to do it.

In general I think the more representations for a GraphQL schema we support and can convert between the better, some people prefer an SDL-first workflow, others code-first.

In general the code sharing could be achieved by moving more things into juniper itself, but I would be more comfortable with modularizing juniper so other libraries can better interoperate with it. In particular I would like to have juniper_query_parser, juniper_schema, and (possibly in one of these) juniper_query_validation. The last one in particular would be very nice to have in graphql_client to do query validation with good error messages at compile time, without duplicating the effort between graphql_client and juniper.

tomhoule avatar Mar 13 '19 21:03 tomhoule

Coming from JS land I like how ergonomic the SDL approach is, but I'm not sure how I feel about the lack of specificity. i.e. How do we interpret a GraphQL Int. Default to an i32 by default? Same for ownership. 🤷‍♀️

vladinator1000 avatar Jul 10 '20 15:07 vladinator1000

@vladinator1000 From the GraphQL spec, the size of a GraphQL Int should represent a value greater than or equal to -2^31 or less than or equal to 2^31. Thus, it's recommended that one use i32 here. If you're using values that fall outside this range, then it's recommended the one use a custom GraphQL scalar type. Int is a default GraphQL scalar type. At this time, I believe that the library doesn't support GraphQL Schema Definition Language (SDL) out of the box and one needs to use Rust types using the Rust language.

conradwt avatar Jul 10 '20 18:07 conradwt

Master supports the SDL as output and another project supports generating Juniper code from the SDL def. See first paragraph of https://graphql-rust.github.io/juniper/master/schema/schemas_and_mutations.html

LegNeato avatar Jul 11 '20 08:07 LegNeato

Ideally you have your schema in a lib crate. This can be used by a binary to dump a .graphql file for other tooling or in a build.rs to do other interesting stuff.

LegNeato avatar Jul 11 '20 08:07 LegNeato