Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schema Stitching & Client generation #5

Open
vektah opened this Issue Feb 14, 2018 · 9 comments

Comments

Projects
None yet
7 participants
@vektah
Copy link
Collaborator

vektah commented Feb 14, 2018

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 https://github.com/shurcooL/githubql?
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.

@vektah vektah added this to the 1.0 milestone Feb 14, 2018

@vektah vektah added the enhancement label Mar 8, 2018

@vektah vektah added this to To do in 1.0 Release Jun 29, 2018

@vektah vektah removed this from the 1.0 milestone Jun 29, 2018

@vektah vektah removed this from Features in 1.0 Release Jul 14, 2018

@aaahrens

This comment has been minimized.

Copy link

aaahrens commented Sep 13, 2018

Is this currently implemented?

@vektah

This comment has been minimized.

Copy link
Collaborator Author

vektah commented Sep 13, 2018

Not yet

@caevv

This comment has been minimized.

Copy link

caevv commented Nov 21, 2018

+1

@sdalezman

This comment has been minimized.

Copy link

sdalezman commented Nov 26, 2018

We're currently doing this within code right now and it's proven to be a bit challenging with edge-cases to support.

We'd be happy to help out on implementing schema stitching, but before jumping in wanted to see if there was an agreed upon design for it before writing any code?

@robinbraemer

This comment has been minimized.

Copy link

robinbraemer commented Dec 26, 2018

Are there any updates?

@robinbraemer

This comment has been minimized.

Copy link

robinbraemer commented Dec 26, 2018

+1

1 similar comment
@codenakama

This comment has been minimized.

Copy link

codenakama commented Jan 11, 2019

+1

@k-kundan

This comment has been minimized.

Copy link

k-kundan commented Jan 22, 2019

Any updates on this?

@sdalezman

This comment has been minimized.

Copy link

sdalezman commented Jan 22, 2019

@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.

stitch: 
  # 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.