-
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
Parameterized fragments #204
Comments
This is a great discussion point; we talked about it for months before we open-sourced, and even now I think there are good arguments either way. To spark the discussion, I've dug up a document that @leebyron wrote up to summarize the options around variables in general (since there were a bunch of things we considered). Idea
Explicit DeclarationFragments define args, and those args must be provided by the reference. query Foo(arg: Number) {
...A(arg: $arg)
}
fragment A(arg: Number) {
field,
...B(arg: $arg)
}
``
fragment B(arg: Number) {
field(arg: $arg)
} Pros:
Cons:
Global DeclarationAll arguments defined in a fragment are implicitly global (passing down is automatic). Query must describe all global variables. query Foo(arg: Number) {
...A
}
fragment A {
field,
...B
}
fragment B(arg: Number) {
field(arg: $arg)
} Pros:
Cons:
Explicit GlobalsAll arguments are defined in a fragment, but must explicitly declare they are put into and come from a global scope. query Foo(global arg: Number) {
...A
}
fragment A {
field,
...B
}
fragment B(global arg: Number) {
field(arg: $arg)
} Pros:
Cons:
Implicit GlobalsAll arguments are defined in a fragment, but the root query defines nothing. query Foo {
...A
}
fragment A {
field,
...B
}
fragment B(arg: Number) {
field(arg: $arg)
} Pros:
Cons:
Always Globals, Always defined (this is what GraphQL today does)All params are defined at the query level and can be used by any fragment. query Foo($arg: Number) {
...A
}
fragment A {
...B
}
fragment B {
field(arg: $arg)
} Pros:
Cons:
Always Globals, Implicit (This is what the original GraphQL at FB did)All params are global, never defined, and can be used by any fragment. query Foo {
...A
}
fragment A {
...B
}
fragment B {
field(arg: $arg)
} Pros:
Cons:
|
As I understand it, a syntactic version of what Relay2 does would be something like this: Globals, Explicit, fragment argumentsAll params are defined at the query level and can be used by any fragment. Additionally, fragment arguments can be defined and a caller can provide them. query Foo($arg: Number) {
...A
}
fragment A {
...B(another: 128)
}
fragment B(another: Number = 64) {
field(arg: $arg, another: $another)
} @josephsavona or @wincent, correct me if I'm wrong here. |
As I recall the conclusion of the discussion prior to the launch, we basically thought that parameterized fragments sounded like a good concept, but that we didn't have a sense of how commonly they would be used, and because of that, the tradeoff of taking on the additional complexity in the core didn't seem worth it. The Relay2 approach seems promising, though, and one of the main reasons we wanted to have directives in the core was to enable experimentation like this. Once we see more use of Relay2 (or other approaches to parameterized fragments), I definitely think we should revisit this decision with the additional knowledge we've gained and see if there's a better option. |
The only issue with Relay2-style experimentation, compared to having stuff like this in the spec, is that it breaks the GraphiQL experience completely. You can no longer debug your queries in an interactive environment and then simply paste them in if all of your queries rely on custom extensions. |
@stubailo The same is true of ES6 code pasted into an older browser. If as a community we limit our language experimentation to only what works in current GraphiQL, we won't be able to experiment much at all. The question is whether we can continue to provide interactive editing environments with new tools. And we can - we could easily build a Relay2 extension to GraphiQL so that people using Relay2 can debug queries. When these features are more established and make their way into the spec, then the need for the custom graphiql would go away. Since GraphiQL can be delivered entirely client-side (querying an existing GraphQL endpoint) and Relay2 compiles down to plain GraphQL, this doesn't even require anything special on the server. |
👍 very cool |
Perhaps we need some kind of tool like Babel, so that people can plug in different GraphQL transformations and use them together? Otherwise we might rapidly run into a world where everyone is using a different flavor of GraphQL, which makes it a lot harder to build great tools, share code between different environments, etc. |
To put it a different way, I think custom extensions to GraphQL are great. IMO having stuff in the spec is even better, but I agree that the spec is not the place to experiment/innovate. But, to take your metaphor about old browsers and JS a different direction: I think it's fine that we all experiment with new features in JavaScript that browsers don't support, and figure out how to make them work. But it would be very frustrating if we had one flavor of JavaScript for our frontend code, one for backend code, one for test code, etc. The greatest benefit of something like Babel for JS is that you can transpile all of your code and it's tool-independent. I'm worried about a situation where there is a Relay-specific flavor of GraphQL that can't be easily consumed by other tools like linters, in-browser auto-complete tools, interactive editors, and more. So perhaps the ideal world would be where the Relay query transformation was open-sourced as a separate tool so that the whole ecosystem of possible tools, including GraphiQL, could use it. |
@stubailo We totally agree, and will open-source the Relay2 compiler as soon as we can! |
@dschafer Already covered the main variations of this, but I'll add a bit more detail about how we're thinking about variables in Relay2. We've found that the encapsulation provided by React components is incredibly useful because it lets developers reason about components in isolation. Developers are able to look at a component and understand how it works without having to read the rest of the codebase - you can reason locally. We feel that it's important to maintain this property with Relay containers, which means that it should be possible to understand container fragments in isolation. This means all variables referenced in a fragment should be defined by that fragment. In current Relay this is achieved by
Based on this we're currently planning to support two main options for fragment argument in Relay2:
Here's an example that demonstrates both:
Note that the
To run this query, developers provide a resolver that, given a provider name such as "PixelDensity.get", can return the runtime value |
I'm curious if there has been any more internal discussion about this. Using Relay modern, I've found it necessary to give globally unique names to my fragment variables, because of the following case. fragment ReactComponentA_friends on User {
id
sessions(first: $ReactComponentA_friends_first, after: $ReactComponentA_friends_after) @connection {
pageInfo {
endCursor
hasNextPage
}
edges {
node {
...something
}
}
}
}
fragment ReactComponentB_pages on User {
id
sessions(first: $ReactComponentB_pages_first, after: $ReactComponentB_pages_after) @connection {
pageInfo {
endCursor
hasNextPage
}
edges {
node {
...something
}
}
}
}
query UserSummary_user () {
user {
# I want the top 100 friends
...ReactComponentA_friends
# I want the top 5 pages
...ReactComponentB_pages
}
} What I would rather do, is: fragment ReactComponentA_friends on User {
id
sessions(first: $first, after: $after) @connection {
pageInfo {
endCursor
hasNextPage
}
edges {
node {
...something
}
}
}
}
fragment ReactComponentB_pages on User {
id
sessions(first: $first, after: $after) @connection {
pageInfo {
endCursor
hasNextPage
}
edges {
node {
...something
}
}
}
}
query UserSummary_user () {
user {
# I want the top 100 friends
...ReactComponentA_friends(first: 100, after: null)
# I want the top 5 pages
...ReactComponentB_pages(first: 5, after: null)
}
} |
Is any progress being made towards this? Something along the following would be very useful:
The current experimental support of variable definitions in fragments simply being hoisted to the parent query leaves much to be desired. |
In my GraphQL system, I currently treat a fragment as a (lambda) function. The parameters are inferred through a standard type check inference algorithm. Your example just makes this into an explicit construction where the parameter passing can be made explicit as well. However, this would mean you have to update the execution phase to include some kind of parameter passing and funcalls as well. Currently, you can get away with keeping the lambda constructions in the type checker only, because in execution the variable environment to draw vars from is static over the query. I've said for a while that GraphQL is just syntactic sugar for a small total lambda calculus: operations and functions and so are fragments. It would be natural to extend this into the language proper at some point. (Aside: This also suggests you can compile GraphQL queries into a small Core language which is functional (lambda calculus + primops) and then execute that in the execution phase. I have a strong hunch this is possible, but I have yet to try rewriting my execution phase to do this). |
We have a use case for a chart We need to get data with custom field name as Day name (Sunday,Monday) same fragment fragment Sales($date:DateTime){
sunday:salesByDate(date:$date){
total
}
}
We need to use date-fns function inside query to get the day name. How can we do that? |
I also got a usecase, but without query variables and rather with fixed fragment values.
It would be nice if I could create a local value fragment which would pass the given value to a nested function.
|
any progress on this? |
to move this RFC along, you can:
|
I have a use case using gatsbyjs/react/graphql where we make countless image queries like this
in many cases we explicitely set the width a/o hight
which can easily be written as
but becomes problematic when used inside another fragment since there doesn't seem to be a way to pass the variable value from a fragment
it would be great to be able to simple do:
I may be wrong, but it seems like the proposed solution would enable this type of fragment nesting which is extremely useful when you have hundreds and hundreds of lines of graphql queries. |
lol It's been 6 years since this issue was first raised, and it's surprising that there hasn't been any noticeable change yet. |
Although standardizing would be great, might be worth mentioning/re-mentioning:
|
Oh... So it isn't supported yet? |
A recent talk about Relay 2 suggested adding arguments to query fragments via directives.
I think first-class support for arguments in fragments would give a few benefits:
For example, a fragment might be defined with variables:
The spread with different values:
To be honest, I don't have a use case. But this seems like a really solid feature, and as a library maintainer, I'd rather add this language feature than implement some new directives! (Or, would those directives be "compiled out"?)
Technically, this could even be backwards compatible. Fragments could also use variables from their outer scope.
What do you think, would this be an improvement? Have I overlooked any downsides or complexities?
The text was updated successfully, but these errors were encountered: