Soluto / stitch

Stitch is a no-code GraphQL tool for your existing APIs and data sources
MIT License
29 stars 8 forks source link

Optimize nested resolvers #79

Open AvivRubys opened 5 years ago

AvivRubys commented 5 years ago

Agogos has enough schema information to be able to avoid requesting data it does not need in the case of nested resolvers.

For example, consider this schema:

type User {
  name: String!
  comments: [String] @http(url: "comment-service/:name")
}

type Query {
  getUser(name: String!): User @http(url: "user-service")
}

And this query:

{
  getUser(name: "elad.aviv") {
    comments
  }
}

In this case, agogos would call user-service and comment-service, even though it doesn't need any data from user-service. Agogos has enough information to avoid this, either through Dataloader and more granular resolvers as described here, or resolver-level analysis that can verify which fields are requested and if they have their own resolvers, avoid the top-level request.

Yshayy commented 5 years ago

It's interesting if the evaluator can put a fake obj placeholder with partial data (can be based args, such as id), and only it there's a need for obj/parent field that is missing it'll query it.

AvivRubys commented 5 years ago

It's interesting if the evaluator can put a fake obj placeholder with partial data (can be based args, such as id), and only it there's a need for obj/parent field that is missing it'll query it.

I think that's the only way this can work. Otherwise, @http or other resolvers would not be able to find the parameters they need. I have a POC of this working, it marks all fields with @http directives on them and then when @http runs, iterates on the subfields and checks if they're marked. If they all are, it skips resolving and returns the args as the result. The problem is that it will only work if the argument names correspond to entity fields, which is too implicit. We could add another directive that maps the arguments to fields. So usually it'll be implicit, but you could make something like this:

type Query {
  getUser(username: String!): User
    @http(url: "user-service/:username")
    @provideField(arg: "username", field: "name")`
}

It's kind of verbose and I'm not sure how it would behave with different orderings of directives but we can probably make it work.

Edit: After sleeping on it, I realized this can still fail if comments would have required an additional field to work. If we could somehow run resolvers in reverse, so each of them could express their dependencies, this would work. Either that or possibly analyzing each resolver's field dependencies? But this starts to feel like a whole lot of very fragile machinery.

AvivRubys commented 5 years ago

More on this: https://github.com/graphql/graphql-js/issues/623