Solvedgqlgen Schema Stitching & Client generation

It would be neat if you could take a schema and generate a strictly typed client from it:

type Schema {
   getUser(id: Int) User

type User {
generate-client schema.graphql -package myclient

It would generate a client allowing easy querying of schemas, carrying selection set information forward in context:

func (r *Resolver) Profile_user(ctx context.Context, parent *Profile) (myclient.User, error) {
	return r.myclient.GetUser(ctx, parent.UserID)

Under the hood the client would generate the query based on the selection set (does it need any type information to generate the query?) and unmarshal the result, looking at __typename where appropriate to create the correct concrete types.

What about
It relies heavily on reflection to generate the query from the struct, but the shape isn't known until runtime so there is nothing to reflect.

21 Answers

✔️Accepted Answer

@vektah we've been thinking a lot about this issue bec we've kind of done this in a round about manner at the moment. We have a few graphql api's on top of our centralized grpahql api. What we noticed is that we typically query these services in isolation and rely on the centralized end point for heavy lifting for things like combining data cross-service.

Let's say we had a user and comments service, then right now we wrap a query around each service so our central query looks like this:

type Query {
    user: User,
    comments: Comments

type User {
    // some query operations

type Comments {
    // some query operations

Our resolvers for users and comments use what we call our query generator. The query generator introspects the fields being requested and generates a new graphql query to the sub-service. That way if let's say we're querying for 4 objects from the user service we could batch them in one request without having 4 different resolvers each with their own http overhead. Again one big reason we did that is we typically aren't querying for comments + users together at the moment.

Our query generator at the moment, is definitely not great and I would say that's probably due to a lack of us truly understanding what's happening under the hood in the graphql.CollectedField. That means we can't use all graphql features because the limitations in our query generator. We've accepted this trade-off because of the advantages of having a centralized graphql service with the sub-routing.

All that being said, given that we've worked on the above for multiple services I think an ideal solution would allow a user to define a schema stitch at the gqlgen level.

  # NOTE: you can define as many stitches as necessary
  serviceName: [some-custom-defined-name]
  serviceURL: [some-url]
  # requestFn allows us to define a custom request generator function so that we can add any authorization headers to our sub service as well as add tracing etc
  requestFn: api.requestFn

gqlgen would then see that and when running the gqlgen generation, it would request that sub-service schema (using the requestFn). It would then merge that schema with the parent schema. It would also save which graphql key belongs to the stitch defined in the yaml. When gqlgen parses a query it would then group together all the queries to that sub-service and send the request to the sub-service, combines all the responses back together and send them back to the user.

In my ideal, ideal world it would also be great if we can have dataloaders between services that are coordinated by this centralized endpoint.

It would also be really cool if we could define custom encoding mechanisms for the child api's. I know graphql whole's thing is http over json, but when we're thinking about sub-service api's and scale, it would be great to be able to use thrift or proto-buffers to encode the requests + responses and save on larger queries.

One other dream I have with this setup is that as we continue to add to our child-graphql-api's it would be great if our parent graphql api was seamlessly updated. Meaning the child would be able to register with the parent that it's schema changed and the parent would automatically update itself. That might be a little difficult to achieve, but as we're speaking about dreams that's on the list. It almost takes you down the path of being able to have children register themselves with the parent graphql api almost in a service discovery mechanism (would definitely make it easier for us to continue to spin up micro-services).

We're definitely running into issues with our current setup and would really like to make progress here. We're happy to help out on a design/building of this feature, but would be great to hear your thoughts first.

Other Answers:

Apollo Federation looks amazing:

Any plans for implementing something like that?

Just wanted to link to as this is exactly what elm-graphql is doing for Elm. cc @dillonkearns

Great! -- it sounds like a number of people are interested in taking a stab at this. Our timeframe is still a few months out probaly -- we need to do a bunch of (mostly non-technical) planning first -- but I think it definitely makes sense for folks to be open when they start working on something, so other interested parties can follow along! This issue is probably as good a place as any to coordinate.

We at Khan Academy are looking to implement apollo federation, and will likely try to do it using gqlgen. We're not ready to start yet, but when the time comes we'd love to have some partners! (If someone else doesn't get to it first... :-) )

More Issues: