Skip to content

Commit 1c38e6a

Browse files
committed
[RFC] Proposed change to directive location introspection
This proposes a change to how we represent the ability to validate the locations of directives via introspection. Specifically, this *removes* `onField`, `onFragment`, and `onOperation` in favor of `locations` which is a list of `__DirectiveLocation`. In addition, this changes the specification of the `@skip` and `@include` directives to no longer allow their use on fragment directives (but are still allowed on fragment spreads and inline fragments). Because of this, a section on "merging fragment directives" is no longer useful. **Rationale:** This allows for a more fine-grained validation of directive placement, now you can assert that a directive is allowed on queries but not mutations, or allowed on fragment definitions but not on fragment spreads. Also, this makes expanding the locations a directive is allowed to be placed easier to do, as future expansions will not affect the introspection API. This should be considered a prereq to supporting directives in more locations. Finally, this is a prereq to a forthcoming RFC to add directives to the type schema language, one of the last missing pieces to represent a full schema using this language. **Drawbacks:** Any change to the introspection API is a challenge. Tools like Graph*i*QL should continue to support the older representation if possible.
1 parent 582eb32 commit 1c38e6a

File tree

4 files changed

+59
-48
lines changed

4 files changed

+59
-48
lines changed

spec/Section 2 -- Language.md

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -909,44 +909,3 @@ and operations.
909909

910910
As future versions of GraphQL adopts new configurable execution capabilities,
911911
they may be exposed via directives.
912-
913-
#### Fragment Directives
914-
915-
Fragments may include directives to alter their behavior. At runtime, the directives provided on a fragment spread override those described on the
916-
definition.
917-
918-
For example, the following query:
919-
920-
```graphql
921-
query hasConditionalFragment($condition: Boolean) {
922-
...maybeFragment @include(if: $condition)
923-
}
924-
925-
fragment maybeFragment on Query {
926-
me {
927-
name
928-
}
929-
}
930-
```
931-
932-
Will have identical runtime behavior as
933-
934-
```graphql
935-
query hasConditionalFragment($condition: Boolean) {
936-
...maybeFragment
937-
}
938-
939-
fragment maybeFragment on Query @include(if: $condition) {
940-
me {
941-
name
942-
}
943-
}
944-
```
945-
946-
FragmentSpreadDirectives(fragmentSpread) :
947-
* Let {directives} be the set of directives on {fragmentSpread}
948-
* Let {fragmentDefinition} be the FragmentDefinition in the document named {fragmentSpread} refers to.
949-
* For each {directive} in directives on {fragmentDefinition}
950-
* If {directives} does not contain a directive named {directive}
951-
* Add {directive} into {directives}
952-
* Return {directives}

spec/Section 3 -- Type System.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -767,8 +767,9 @@ GraphQL implementations should provide the `@skip` and `@include` directives.
767767

768768
### @skip
769769

770-
The `@skip` directive may be provided for fields or fragments, and allows
771-
for conditional exclusion during execution as described by the if argument.
770+
The `@skip` directive may be provided for fields, fragment spreads, and
771+
inline fragments, and allows for conditional exclusion during execution as
772+
described by the if argument.
772773

773774
In this example `experimentalField` will be queried only if the `$someTest` is
774775
provided a `false` value.
@@ -781,8 +782,9 @@ query myQuery($someTest: Boolean) {
781782

782783
### @include
783784

784-
The `@include` directive may be provided for fields or fragments, and allows
785-
for conditional inclusion during execution as described by the if argument.
785+
The `@include` directive may be provided for fields, fragment spreads, and
786+
inline fragments, and allows for conditional inclusion during execution as
787+
described by the if argument.
786788

787789
In this example `experimentalField` will be queried only if the `$someTest` is
788790
provided a `true` value.

spec/Section 4 -- Introspection.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,18 @@ enum __TypeKind {
181181
type __Directive {
182182
name: String!
183183
description: String
184+
locations: [__DirectiveLocation!]!
184185
args: [__InputValue!]!
185-
onOperation: Boolean!
186-
onFragment: Boolean!
187-
onField: Boolean!
186+
}
187+
188+
enum __DirectiveLocation {
189+
QUERY
190+
MUTATION
191+
SUBSCRIPTION
192+
FIELD
193+
FRAGMENT_DEFINITION
194+
FRAGMENT_SPREAD
195+
INLINE_FRAGMENT
188196
}
189197
```
190198

@@ -375,3 +383,17 @@ Fields
375383
* `defaultValue` may return a String encoding (using the GraphQL language) the
376384
default value used by this input value in the condition a value is not
377385
provided at runtime. If this input value has no default value, returns {null}.
386+
387+
388+
### The __Directive Type
389+
390+
The `__Directive` type represents a Directive that a server supports.
391+
392+
Fields
393+
394+
* `name` must return a String
395+
* `description` may return a String or {null}
396+
* `locations` returns a List of `__DirectiveLocation` representing the valid
397+
locations this directive may be placed.
398+
* `args` returns a List of `__InputValue` representing the arguments this
399+
directive accepts.

spec/Section 5 -- Validation.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,34 @@ For example the following query will not pass validation.
11001100
GraphQL servers define what directives they support. For each
11011101
usage of a directive, the directive must be available on that server.
11021102

1103+
1104+
### Directives Are In Valid Locations
1105+
1106+
** Formal Specification **
1107+
1108+
* For every {directive} in a document.
1109+
* Let {directiveName} be the name of {directive}.
1110+
* Let {directiveDefinition} be the directive named {directiveName}.
1111+
* Let {locations} be the valid locations for {directiveDefinition}.
1112+
* Let {adjacent} be the AST node the directive effects.
1113+
* {adjacent} must be represented by an item within {locations}.
1114+
1115+
** Explanatory Text **
1116+
1117+
GraphQL servers define what directives they support and where they support them.
1118+
For each usage of a directive, the directive must be used in a location that the
1119+
server has declared support for.
1120+
1121+
For example the following query will not pass validation because `@skip` does
1122+
not provide `QUERY` as a valid location.
1123+
1124+
```!graphql
1125+
query @skip(if: $foo) {
1126+
field
1127+
}
1128+
```
1129+
1130+
11031131
## Variables
11041132

11051133
### Variable Uniqueness

0 commit comments

Comments
 (0)