Fragment arguments/variables (syntax/validation/execution) - #1081
Fragment arguments/variables (syntax/validation/execution)#1081JoviDeCroock wants to merge 54 commits into
Conversation
✅ Deploy Preview for graphql-spec-draft ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
mjmahone
left a comment
There was a problem hiding this comment.
This is clear to me :)
Very excited to see progress, and thank you for cleaning up and clarifying the PR!
5a73543 to
d7590fa
Compare
|
@graphql/tsc is there anything I can do to move this forward? |
benjie
left a comment
There was a problem hiding this comment.
It feels like section 2 should comment on the fact that shadowed variables may not be referenced implicitly by child fragment spreads. (This is probably not the right way of saying that!) E.g. in the following query, Forbidden is spread inside of F and attempts to use variable $a which is ambiguous, and hence not permitted.
query Q($a: Int!, $b: Int!) {
...F(a: 7)
}
fragment F($a: Int!) {
...Fine
...Forbidden
}
fragment Fine {
b: echo(input: $b)
}
fragment Forbidden {
a: echo(input: $a)
}|
@benjie from a purely technical perspective I agree, I was approaching that more from a backwards compatibility perspective. The Fragments variables are only applicable within the context of a i.e. This also makes Fragment-Arguments easier to reason about for me atleast as it's either the Variables passed into the definition or from the operation itself. |
ac9fdbc to
03ba255
Compare
benjie
left a comment
There was a problem hiding this comment.
I haven't got as far as section 6 yet.
|
@JoviDeCroock Ah! I thought we landed on the other side with this one, this does simplify things significantly because we can look at fragments in isolation 👍 |
benjie
left a comment
There was a problem hiding this comment.
Great work Jovi! Here's a few comments, but this was only a quick review so it's not exhaustive.
|
@JoviDeCroock did you rebase on a different computer? Often this happens when |
|
@benjie I did not, the commits it points at are code-suggestions I applied so maybe it's not good at the co-authored ones? Not sure 😅 can rebase to fix it |
dc25c04 to
f306736
Compare
…l#4015) This is a rebase of graphql#3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
…l#4015) This is a rebase of graphql#3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
…l#4015) This is a rebase of graphql#3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
…l#4015) This is a rebase of graphql#3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
…l#4015) This is a rebase of graphql#3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
…l#4015) This is a rebase of graphql#3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
This is a rebase of #3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
This is a rebase of #3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
…l#4015) This is a rebase of graphql#3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
## Summary
- Add comprehensive RFC for building an independent GraphQL LSP for
soda-gql
- Add supplementary implementation details report
### Key design decisions
- **Callback + Tagged Template API**: `gql.{schemaName}(({ query,
fragment }) => query`...`)` — callback required for intermediate module
compatibility, tag names encode operation kind
- **Fragment Arguments RFC syntax**: Fragment variables declared in
GraphQL via [spec proposal
#1081](graphql/graphql-spec#1081)
- **Metadata chaining**: Tagged template results are callable for
attaching metadata
- **Hybrid LSP**: `vscode-languageserver` + `graphql-language-service`
interface layer (not server) + SWC parser
- **SWC for TS parsing**: Fast AST analysis for always-on LSP
### Documents
| File | Description |
|------|-------------|
| `docs/rfcs/graphql-lsp-multi-schema.md` | Main RFC: motivation, API
design, LSP architecture, implementation plan |
| `docs/rfcs/graphql-lsp-implementation-details.md` | Supplementary: LSP
protocol requirements, graphql-language-service analysis, extension
opportunities |
### Open questions (to be resolved during implementation)
- Type inference strategy (TypedDocumentNode vs $infer)
- Fragment cross-file resolution approach
- Schema reload strategy
## Test plan
- [ ] Review RFC for consistency and completeness
- [ ] Validate code examples match existing soda-gql patterns
- [ ] Confirm Fragment Arguments RFC syntax is correctly described
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This is a rebase of #3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
This is a rebase of #3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
This is a rebase of #3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
|
Apollo Kotlin 5.0.0-alpha.5+ supports fragment arguments (PR) |
This is a rebase of #3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
This is a rebase of #3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
This is a rebase of #3847 This implements execution of Fragment Arguments, and more specifically visiting, parsing and printing of fragment-spreads with arguments and fragment definitions with variables, as described by the spec changes in graphql/graphql-spec#1081. There are a few amendments in terms of execution and keying the fragment-spreads, these are reflected in mjmahone/graphql-spec#3 The purpose is to be able to independently review all the moving parts, the stacked PR's will contain mentions of open feedback that was present at the time. - [execution changes](JoviDeCroock#2) - [TypeInfo & validation changes](JoviDeCroock#4) - [validation changes in isolation](JoviDeCroock#5) CC @mjmahone the original author --------- Co-authored-by: mjmahone <mahoney.mattj@gmail.com> Co-authored-by: Yaacov Rydzinski <yaacovCR@gmail.com>
|
Thanks everyone for the discussion and work on this PR. I won’t be able to continue championing or spending time on this proposal. If there’s still interest in moving this forward, it would likely benefit from someone else picking it up and driving it. Appreciate all the input so far, and happy to see it continue if someone wants to take it further. |
|
Thanks for your work on this, @JoviDeCroock! I've re-opened it in #1224 to ensure the work isn't lost since it's RFC2; I've marked it as "needs champion". It already feels like it's fairly close to the finish line thanks to your hard work! |
The initial v17 release included validation of fragment arguments using an earlier, rejected version of the field selection merging rules. This PR updates fragment arguments validation to conform to the current spec proposal. The proposal intentionally requires variables in otherwise identical arguments to refer to the same variable definition. Following argument values through fragment spreads could allow more documents, but it makes the logic for determining whether arguments are the same significantly more complex. The current fragment arguments proposal proposal takes the conservative approach of rejecting these cases. References: - Current fragment arguments proposal: graphql/graphql-spec#1224 - Earlier (now-closed) fragment arguments proposal: graphql/graphql-spec#1081 - Benjie's rationale for the conservative validation rule: graphql/graphql-spec#1081 (comment) - Benjie's follow-up PR updating the proposal: JoviDeCroock/graphql-spec#2 - Proposal commit requiring references to the same variable definition: graphql/graphql-spec@6659416 - Initial GraphQL.js fragment arguments implementation: graphql#4015
The initial v17 release included validation of fragment arguments using an earlier, rejected version of the field selection merging rules. This PR updates fragment arguments validation to the current spec proposal.
The current spec proposal intentionally requires variables in otherwise identical arguments to refer to the same variable definition. Following argument values through fragment spreads could allow more documents, but it makes the logic for determining whether arguments are the same significantly more complex. The current fragment arguments proposal takes the conservative approach of rejecting these cases.
For example, these fields appear to use the same operation variable:
```graphql
query Example($size: Int!) {
user {
image(size: $size)
...Image(size: $size)
}
}
fragment Image($size: Int!) on User {
image(size: $size)
}
```
The first field uses the operation variable, while the field in `Image` uses the fragment variable. Treating them as equal requires resolving the spread argument before comparing the fields.
A literal may also match a fragment variable default:
```graphql
query Example {
user {
image(size: 10)
...Image
}
}
fragment Image($size: Int = 10) on User {
image(size: $size)
}
```
Treating these fields as equal requires applying the omitted fragment argument default during field selection merging.
Bindings may also pass through several fragments:
```graphql
query Example($size: Int!) {
user {
image(size: $size)
...Outer(size: $size)
}
}
fragment Outer($size: Int!) on User {
...Image(size: $size)
}
fragment Image($size: Int!) on User {
image(size: $size)
}
```
Treating these fields as equal requires following the operation variable through both fragment variable definitions.
Allowing these cases means a fragment cannot be checked on its own. The validator must carry variable bindings into each spread, apply defaults, follow bindings through nested fragments, cache the same fragment separately for different bindings, and handle cycles while doing so. Whether two arguments are the same then depends on the full path to the fragment.
Requiring the same variable definition keeps the check local. Variables are compared by their definition rather than by tracing the values passed to them. The stricter rule can also be relaxed later without invalidating documents that are already valid, but v17 incorrectly released the more lenient rule despite the currently stricter stance within the specification.
References:
- Current fragment arguments proposal: graphql/graphql-spec#1224
- Earlier (now-closed) fragment arguments proposal: graphql/graphql-spec#1081
- Benjie's rationale for the conservative validation rule: graphql/graphql-spec#1081 (comment)
- Benjie's follow-up PR updating the proposal: JoviDeCroock/graphql-spec#2
- Proposal commit requiring references to the same variable definition: graphql/graphql-spec@6659416
- Initial GraphQL.js fragment arguments implementation: graphql#4015
…#4839) The initial v17 release included validation of fragment arguments using an earlier, rejected version of the field selection merging rules. This PR updates fragment arguments validation to the current spec proposal. The current spec proposal intentionally requires variables in otherwise identical arguments to refer to the same variable definition. Following argument values through fragment spreads could allow more documents, but it makes the logic for determining whether arguments are the same significantly more complex. The current fragment arguments proposal takes the conservative approach of rejecting these cases. For example, these fields appear to use the same operation variable: ```graphql query Example($size: Int!) { user { image(size: $size) ...Image(size: $size) } } fragment Image($size: Int!) on User { image(size: $size) } ``` The first field uses the operation variable, while the field in `Image` uses the fragment variable. Treating them as equal requires resolving the spread argument before comparing the fields. A literal may also match a fragment variable default: ```graphql query Example { user { image(size: 10) ...Image } } fragment Image($size: Int = 10) on User { image(size: $size) } ``` Treating these fields as equal requires applying the omitted fragment argument default during field selection merging. Bindings may also pass through several fragments: ```graphql query Example($size: Int!) { user { image(size: $size) ...Outer(size: $size) } } fragment Outer($size: Int!) on User { ...Image(size: $size) } fragment Image($size: Int!) on User { image(size: $size) } ``` Treating these fields as equal requires following the operation variable through both fragment variable definitions. Allowing these cases means a fragment cannot be checked on its own. The validator must carry variable bindings into each spread, apply defaults, follow bindings through nested fragments, cache the same fragment separately for different bindings, and handle cycles while doing so. Whether two arguments are the same then depends on the full path to the fragment. Requiring the same variable definition keeps the check local. Variables are compared by their definition rather than by tracing the values passed to them. The stricter rule can also be relaxed later without invalidating documents that are already valid, but v17 incorrectly released the more lenient rule despite the currently stricter stance within the specification. References: - Current fragment arguments proposal: graphql/graphql-spec#1224 - Earlier (now-closed) fragment arguments proposal: graphql/graphql-spec#1081 - Benjie's rationale for the conservative validation rule: graphql/graphql-spec#1081 (comment) - Benjie's follow-up PR updating the proposal: JoviDeCroock/graphql-spec#2 - Proposal commit requiring references to the same variable definition: graphql/graphql-spec@6659416 - Initial GraphQL.js fragment arguments implementation: #4015
This spec contains amendments to #1010, a diffed view is available at mjmahone#3.
These amendments are made from comments on the implementation PR and alterations from the new implementation
coercelogic we use in the general flowIn general the biggest changes are that we introduce
fragmentVariableValueswhich will be on thegroupedFieldSet, these are derived from the arguments in scope of the fragmentDefinition where this field is used.We introduce
localFragmentVariableswhich as we are traversing down fragment-spreads are a coerced set of variables i.e.Last but not least we introduce
getArgumentValuesFromSpreadwhich looks at the spread and fragment-definition and establishes a coerced set of localVariableValues.