Skip to content

Support @skip and @include on fields only - #146

Closed
spawnia wants to merge 2 commits into
masterfrom
client-directives-field-only
Closed

spawnia wants to merge 2 commits into
masterfrom
client-directives-field-only

Conversation

@spawnia

@spawnia spawnia commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Part of #149

A field with @skip or @include may be missing from the response, which Sailor rejects today.
This supports the directives on fields only, and fails code generation everywhere else.

Alternatives: #79 (full support), #148 (full support on the merged field model), #143 (reject the directives)

A field with the directive becomes nullable and may be absent

email @skip(if: $skipEmail) generates string|null, and make() takes it as an optional argument.
The converter is wrapped in OmittableConverter, and ObjectLike::fromGraphQL() skips a missing field only for that wrapper.
NonNullConverter stays inside, so a present explicit null on a non-null field still fails.

Code generation fails for directives on inline fragments and fragment spreads

FoldFragments throws before it inlines a spread, so the message names the spread rather than the inline fragment it would become:

  • Sailor only supports @skip and @include on fields, found @skip on fragment spread ...TwoArgs.
  • Sailor only supports @skip and @include on fields, found @include on inline fragment on Query.

The workaround is to move the directive onto the fields.

Rule: a field with the directive must be the only selection at its response path

The response path is the chain of response names from the operation to the field, through fragments and regardless of type conditions.
With a @skip(if: $x) { b } a { c }, the server returns a with only c, so b would be missing from a present a.
The same applies when an ancestor is selected twice, or a fragment selects the field again.

Codegen fails with ... found @skip on Foo.singleObject which is selected 2 times. Give the selections distinct aliases.

The rule also rejects some sound queries, such as the same field under two different type conditions, or a @skip(if: $x) a.
Distinct aliases fix all of them.

Literal arguments get no special case

@skip(if: false) and @include(if: true) also make the field nullable, and also fail on fragments.
This is looser than needed, but keeps the code free of argument evaluation.

The omission logic is about half the size of #79

git diff --stat origin/master...HEAD -- src: 7 files, 143 insertions, 10 deletions.
#79: 6 files, 278 insertions, 34 deletions.

Operations that generated before may now fail codegen

ClientDirectiveFragmentSpreadQuery and ClientDirectiveInlineFragmentQuery in examples/simple now fail, so this removes them.
They already failed at runtime with Missing field whenever the server skipped the fragment.

🤖 Generated with Claude Code

Fields with @Skip or @include become nullable and may be absent from the
response. Codegen fails for the directives on fragments and on fields
selected more than once under the same response path.

🤖 Generated with Claude Code
🤖 Generated with Claude Code
@spawnia

spawnia commented Oct 6, 2026

Copy link
Copy Markdown
Owner Author

🤖 Superseded by #148, which also supports the directives on inline fragments and fragment spreads.

@spawnia spawnia closed this Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant