-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add support for fragment aliases #137
Comments
Thanks for keeping this open! I think we may come back to this as we work out our 2016 roadmap. We considered this feature for the initial version of the spec, but there are some pretty serious concerns to weigh. The primary concern is response confusion. This feature can be powerful, but it can also lead to harder to understand queries. Right now one JSON object always corresponds to one GraphQL object, but this feature allows for deeply nested The second concern is it can become easy to accidentally produce field over-fetching. For example consider:
The result of this will be duplicate querying and returning of the Considering that one major motivation for this is more safely separating fragments from each other so they are unlikely to overlap, this motivation is exactly what would cause this duplicate over-fetching to occur. We should keep the issue open though as a place to collect discussion, and perhaps reconsider should we come up with new response formats that makes such a thing possible. |
Closing this aging issue. I think this could be re-proposed should a champion be compelled. |
Hello @leebyron! I would like to re-propose and champion this issue. What would you recommend as the first step for that? BackgroundHere is the reason I am interested in championing this feature. I'm the author of a GraphQL client for Elm. The library abstracts away aliases for you and deserializes the GraphQL responses into Elm data structures based on your Elm request code. Since the data is turned into Elm data structures (not accessed directly as raw JSON as in JS clients), aliases are treated as an implementation detail of the Elm GraphQL client library. So the Elm client is responsible for creating aliases that result in a valid request. For example, it will automatically use an alias for fields based its arguments to ensure there are no collisions (I discuss it in detail in this post if you're curious!). And it's worth noting that the auto-generated aliases need to be independent of context (for reasons which I go into in the aforementioned post). This works great, but I recently got a bug report regarding Counter Example 112: # Counter Example No. 112
fragment conflictingDifferingResponses on Pet {
... on Dog {
someValue: nickname
}
... on Cat {
someValue: meowVolume
}
} The problem is that if a type condition returns two different types under the same response key, there is an error from the GraphQL server (as the spec says it should). Here's the full issue: dillonkearns/elm-graphql#95. If you want more detail about how my Elm GraphQL client automatically generates unique aliases, you can see dillonkearns/elm-graphql#64 (comment). The best solution I can think of to this problem for my library would be introducing Fragment Aliases. With this feature available, I would simply use a Fragment Alias that included the type of the fragment in the alias name. This would avoid the merge collision error here. I'm open to other ideas about how to solve this problem as well! Again, please let me know what the best way to proceed is here. And thank you for such a wonderful and well-maintained project! |
@leebyron another way I could solve the problem I described above would be to use an alias with it the typename appended for each scalar field (such as This is totally doable, and I'm open to taking that approach. And perhaps Fragment Type Aliases wouldn't get at the root problem. I think it might help me if I understood the intention behind Counter Example No. 112 a little better. It makes perfect sense that fields can't be merged if they have different arguments, or if they alias to the same name but are different fields. But in the case of type conditions like counter example 112, there is no conflict because the data isn't actually being merged. I was surprised initially to find that this caused an error because merging seems like a "merge this and that together" sort of thing, whereas with type conditions it's more like "return back this, or return back that (but never both)". That is, it doesn't ever combine anything because the responses are mutually exclusive, so they could have different shapes. So what I'm trying to understand better is what is the reason for disallowing these cases with different shapes within type conditions? Was the intention of this design to avoid user error by telling them that types are different? Or something else? I appreciate any clarifications you can give! |
@dillonkearns You can find answer inside commit that added it d481d17:
So basically without this rule, it's not possible to generate simple native data structures for strongly typed languages which is very important for mobile clients. |
@IvanGoncharov thank you so much for the clarification! That's really helpful 👍
That makes sense. Elm is strongly typed, but my client, https://github.com/dillonkearns/elm-graphql, doesn't have this same issue because it generates code for you to build up your own queries. I think the type of client you're describing would take the approach of generating code based on a specific raw GraphQL String. Another way to solve that problem could have been to let the clients give errors in such cases and not output any code unless it was disambiguated. But since some clients are depending on this behavior now, it would be a major breaking change so it's not worth considering at this point. It's not ideal, but it sounds like the best approach for me is to use the typename in aliases for scalars as I described above. I wish there was a way where I didn't have to add more noise to the generated queries, but it will definitely get the job done. Thank you again for clarifying the rationale for that part of the spec! |
@danielkwinsor This is an exact purpose for validation. Idea is that you validate your queries inside client source code and not on the server and AFAIK Facebook doing exactly that using persisted queries. https://facebook.github.io/graphql/June2018/#sec-Validation
|
Hello @IvanGoncharov, thank you for taking the time to write these detailed responses, I really appreciate it! I'm not sure if I'm understanding this correctly, but it looks like these requests still will be rejected by the server framework, right? Except in some cases where the server framework can choose to skip a validation for the purpose of backwards-compatibility?
(The bold is mine). Does the bold section mean that the server should give an error, or is it only the clients that should provide errors (sort of like an optional linter warning). In other words, should these be errors or warnings on the server? I was able to confirm that Elixir/Absinthe returns a 200 when making a request that fails this validation (you can run the request yourself here). Is this an exception, or should every implementation give a 200 status for this request? |
@dillonkearns Execution and Validation are fully separate operations. Validation should be done at some point but:
It is possible since validation is fully independent of execution and doesn't require anything except schema (that you can get using introspection) so you can fully validate query without sending it to the server. A good example is ESLint https://github.com/apollographql/eslint-plugin-graphql So returning to the original question:
Instead of defining separate validation rules for every tool/server GraphQL defines a common set of rules for the entire ecosystem. That means |
Hey @IvanGoncharov, yes that makes sense that GraphQL syntax limits what is valid in order to make any valid GraphQL query work for codegen tools. Thank you so much for helping me understand the rationale here, that's really helpful in my design process for my own GraphQL codegen tool! I've gone ahead and implemented a solution to the problem I described earlier using the typename in field aliases. It's working well (just a bit noisy in the demos)! Thanks again for all your replies here Ivan! |
Saw this idea referenced in a Relay ticket: facebook/relay#309 (comment)
Thought it might be useful to open a ticket here as a reminder or for more discussion.
The text was updated successfully, but these errors were encountered: