|
1 |
| ---- |
2 |
| -id: directives |
3 |
| -title: Directives |
4 |
| ---- |
5 |
| - |
6 |
| -GraphQL directives can be used to transform the schema types, fields and arguments as well as modify the runtime |
7 |
| -behavior of the query (e.g. implement access control, etc). Common use cases involve limiting functionality based on the |
8 |
| -user authentication and authorization. While [GraphQL |
9 |
| -spec](https://graphql.github.io/graphql-spec/draft/#sec-Type-System.Directives) specifies two types of directives - |
10 |
| -`executable` (aka query) and `type system` (aka schema) directives, only the latter one is supported by |
11 |
| -`graphql-kotlin-schema-generator`. |
12 |
| - |
13 |
| -## Default Directives |
14 |
| - |
15 |
| -`@deprecated` - schema directive used to represent deprecated portion of the schema. |
16 |
| -See [@Deprecated](customizing-schemas/evolving-schema) annotation documentation for more details |
17 |
| - |
18 |
| -```graphql |
19 |
| -type Query { |
20 |
| - deprecatedQuery: Boolean! @deprecated(reason: "No longer supported") |
21 |
| -} |
22 |
| -``` |
23 |
| - |
24 |
| -`@skip` - query directive that allows for conditional exclusion of fields or fragments |
25 |
| - |
26 |
| -```graphql |
27 |
| -query myQuery($shouldSkip: Boolean) { |
28 |
| - myField @skip(if: $shouldSkip) |
29 |
| -} |
30 |
| -``` |
31 |
| - |
32 |
| -`@include` - query directive that allows for conditional inclusion of fields or fragments |
33 |
| - |
34 |
| -```graphql |
35 |
| -query myQuery($shouldInclude: Boolean) { |
36 |
| - myField @include(if: $shouldInclude) |
37 |
| -} |
38 |
| -``` |
39 |
| - |
40 |
| -## Custom Directives |
41 |
| - |
42 |
| -Custom directives can be added to the schema using custom annotations: |
43 |
| - |
44 |
| -```kotlin |
45 |
| -@GraphQLDirective( |
46 |
| - name = "awesome", |
47 |
| - description = "This element is great", |
48 |
| - locations = [FIELD_DEFINITION] |
49 |
| -) |
50 |
| -annotation class AwesomeDirective(val value: String) |
51 |
| - |
52 |
| -class MyQuery { |
53 |
| - @AwesomeDirective("cool stuff") |
54 |
| - val somethingGreat: String = "Hello World" |
55 |
| -} |
56 |
| -``` |
57 |
| - |
58 |
| -The directive will then added to the schema as: |
59 |
| - |
60 |
| -```graphql |
61 |
| -# This element is great |
62 |
| -directive @awesome(value: String) on FIELD_DEFINITION |
63 |
| - |
64 |
| -type MyQuery { |
65 |
| - somethingGreat: String @awesome("cool stuff") |
66 |
| -} |
67 |
| -``` |
68 |
| - |
69 |
| -Directives can be added to various places in the schema. See the |
70 |
| -[graphql.introspection.Introspection.DirectiveLocation](https://github.com/graphql-java/graphql-java/blob/v13.0/src/main/java/graphql/introspection/Introspection.java#L332) |
71 |
| -enum from `graphql-java` for a full list of valid locations. |
72 |
| - |
73 |
| -**Note that GraphQL directives are currently not available through introspection and you have to use SDL directly |
74 |
| -instead (you can use convenient `print` extension function of `GraphQLSchema`)**. See [GraphQL |
75 |
| -issue](https://github.com/facebook/graphql/issues/300) and corresponding [graphql-java |
76 |
| -issue](https://github.com/graphql-java/graphql-java/issues/1017) for more details about the introspection issue. |
77 |
| - |
78 |
| -### Naming Convention |
79 |
| - |
80 |
| -As described in the example above, the directive name in the schema will by default come from the |
81 |
| -`@GraphQLDirective.name` attribute which should follow `lowerCamelCase` format. If this value is not specified, the |
82 |
| -directive name will default to the normalized decapitalized name of the annotated annotation (eg: `awesomeDirective` in |
83 |
| -the example above). |
84 |
| - |
85 |
| -### Customizing Behavior |
86 |
| - |
87 |
| -Directives allow you to customize the behavior of your schema based on some predefined conditions. Simplest way to |
88 |
| -modify the default behavior of your GraphQLTypes is by providing your custom `KotlinSchemaDirectiveWiring` through |
89 |
| -`KotlinDirectiveWiringFactory` factory used by your `SchemaGeneratorHooks`. |
90 |
| - |
91 |
| -Example of a directive that converts field to lowercase |
92 |
| - |
93 |
| -```kotlin |
94 |
| -@GraphQLDirective(name = "lowercase", description = "Modifies the string field to lowercase") |
95 |
| -annotation class LowercaseDirective |
96 |
| - |
97 |
| -class LowercaseSchemaDirectiveWiring : KotlinSchemaDirectiveWiring { |
98 |
| - |
99 |
| - override fun onField(environment: KotlinFieldDirectiveEnvironment): GraphQLFieldDefinition { |
100 |
| - val field = environment.element |
101 |
| - val originalDataFetcher: DataFetcher<Any> = environment.getDataFetcher() |
102 |
| - |
103 |
| - val lowerCaseFetcher = DataFetcherFactories.wrapDataFetcher( |
104 |
| - originalDataFetcher, |
105 |
| - BiFunction<DataFetchingEnvironment, Any, Any>{ _, value -> value.toString().toLowerCase() } |
106 |
| - ) |
107 |
| - environment.setDataFetcher(lowerCaseFetcher) |
108 |
| - return field |
109 |
| - } |
110 |
| -} |
111 |
| -``` |
112 |
| - |
113 |
| -While you can manually apply all the runtime wirings to the corresponding GraphQL types directly in |
114 |
| -`SchemaGeneratorHooks#onRewireGraphQLType`, we recommend the usage of our |
115 |
| -[KotlinDirectiveWiringFactory](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/directives/KotlinDirectiveWiringFactory.kt) |
116 |
| -to simplify the integrations. `KotlinDirectiveWiringFactory` accepts a mapping of directives to corresponding wirings or |
117 |
| -could be extended to provide the wirings through `KotlinDirectiveWiringFactory#getSchemaDirectiveWiring` that accepts |
118 |
| -`KotlinSchemaDirectiveEnvironment`. |
119 |
| - |
120 |
| -```kotlin |
121 |
| -val queries = ... |
122 |
| -val customWiringFactory = KotlinDirectiveWiringFactory( |
123 |
| - manualWiring = mapOf<String, KotlinSchemaDirectiveWiring>("lowercase" to LowercaseSchemaDirectiveWiring())) |
124 |
| -val customHooks = object : SchemaGeneratorHooks { |
125 |
| - override val wiringFactory: KotlinDirectiveWiringFactory |
126 |
| - get() = customWiringFactory |
127 |
| -} |
128 |
| -val schemaGeneratorConfig = SchemaGeneratorConfig(hooks = customHooks) |
129 |
| -val schema = toSchema(queries = queries, config = schemaGeneratorConfig) |
130 |
| -``` |
131 |
| - |
132 |
| -While providing directives on different schema elements you will be able to modify the underlying GraphQL types. Keep in |
133 |
| -mind though that data fetchers are used to resolve the fields so only field directives (and by association their |
134 |
| -arguments directives) can modify runtime behavior based on the context and user input. |
135 |
| - |
136 |
| -**NOTE: `graphql-kotlin` prioritizes manual wiring mappings over the wirings provided by the |
137 |
| -`KotlinDirectiveWiringFactory#getSchemaDirectiveWiring`. This is a different behavior than `graphql-java` which will |
138 |
| -first attempt to use `WiringFactory` and then fallback to manual overrides.** |
139 |
| - |
140 |
| -For more details please refer to the example usage of directives in our [example |
141 |
| -app](https://github.com/ExpediaGroup/graphql-kotlin/tree/master/examples/spring). |
142 |
| - |
143 |
| -## Directive Chaining |
144 |
| - |
145 |
| -Directives are applied in the order annotations are declared on the given object. Given |
146 |
| - |
147 |
| -```kotlin |
148 |
| - @Directive1 |
149 |
| - @Directive2 |
150 |
| - fun doSomething(): String { |
151 |
| - // does something |
152 |
| - } |
153 |
| -``` |
154 |
| - |
155 |
| -`Directive1` will be applied first followed by the `Directive2`. |
| 1 | +--- |
| 2 | +id: directives |
| 3 | +title: Directives |
| 4 | +--- |
| 5 | + |
| 6 | +GraphQL directives can be used to transform the schema types, fields and arguments as well as modify the runtime |
| 7 | +behavior of the query (e.g. implement access control, etc). Common use cases involve limiting functionality based on the |
| 8 | +user authentication and authorization. While [GraphQL |
| 9 | +spec](https://graphql.github.io/graphql-spec/draft/#sec-Type-System.Directives) specifies two types of directives - |
| 10 | +`executable` (aka query) and `type system` (aka schema) directives, only the latter one is supported by |
| 11 | +`graphql-kotlin-schema-generator`. |
| 12 | + |
| 13 | +## Default Directives |
| 14 | + |
| 15 | +`@deprecated` - schema directive used to represent deprecated portion of the schema. |
| 16 | +See [@Deprecated](customizing-schemas/deprecating-schema) annotation documentation for more details |
| 17 | + |
| 18 | +```graphql |
| 19 | +type Query { |
| 20 | + deprecatedQuery: Boolean! @deprecated(reason: "No longer supported") |
| 21 | +} |
| 22 | +``` |
| 23 | + |
| 24 | +`@skip` - query directive that allows for conditional exclusion of fields or fragments |
| 25 | + |
| 26 | +```graphql |
| 27 | +query myQuery($shouldSkip: Boolean) { |
| 28 | + myField @skip(if: $shouldSkip) |
| 29 | +} |
| 30 | +``` |
| 31 | + |
| 32 | +`@include` - query directive that allows for conditional inclusion of fields or fragments |
| 33 | + |
| 34 | +```graphql |
| 35 | +query myQuery($shouldInclude: Boolean) { |
| 36 | + myField @include(if: $shouldInclude) |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +## Custom Directives |
| 41 | + |
| 42 | +Custom directives can be added to the schema using custom annotations: |
| 43 | + |
| 44 | +```kotlin |
| 45 | +@GraphQLDirective( |
| 46 | + name = "awesome", |
| 47 | + description = "This element is great", |
| 48 | + locations = [FIELD_DEFINITION] |
| 49 | +) |
| 50 | +annotation class AwesomeDirective(val value: String) |
| 51 | + |
| 52 | +class MyQuery { |
| 53 | + @AwesomeDirective("cool stuff") |
| 54 | + val somethingGreat: String = "Hello World" |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +The directive will then added to the schema as: |
| 59 | + |
| 60 | +```graphql |
| 61 | +# This element is great |
| 62 | +directive @awesome(value: String) on FIELD_DEFINITION |
| 63 | + |
| 64 | +type MyQuery { |
| 65 | + somethingGreat: String @awesome("cool stuff") |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +Directives can be added to various places in the schema. See the |
| 70 | +[graphql.introspection.Introspection.DirectiveLocation](https://github.com/graphql-java/graphql-java/blob/v13.0/src/main/java/graphql/introspection/Introspection.java#L332) |
| 71 | +enum from `graphql-java` for a full list of valid locations. |
| 72 | + |
| 73 | +**Note that GraphQL directives are currently not available through introspection and you have to use SDL directly |
| 74 | +instead (you can use convenient `print` extension function of `GraphQLSchema`)**. See [GraphQL |
| 75 | +issue](https://github.com/facebook/graphql/issues/300) and corresponding [graphql-java |
| 76 | +issue](https://github.com/graphql-java/graphql-java/issues/1017) for more details about the introspection issue. |
| 77 | + |
| 78 | +### Naming Convention |
| 79 | + |
| 80 | +As described in the example above, the directive name in the schema will by default come from the |
| 81 | +`@GraphQLDirective.name` attribute which should follow `lowerCamelCase` format. If this value is not specified, the |
| 82 | +directive name will default to the normalized decapitalized name of the annotated annotation (eg: `awesomeDirective` in |
| 83 | +the example above). |
| 84 | + |
| 85 | +### Customizing Behavior |
| 86 | + |
| 87 | +Directives allow you to customize the behavior of your schema based on some predefined conditions. Simplest way to |
| 88 | +modify the default behavior of your GraphQLTypes is by providing your custom `KotlinSchemaDirectiveWiring` through |
| 89 | +`KotlinDirectiveWiringFactory` factory used by your `SchemaGeneratorHooks`. |
| 90 | + |
| 91 | +Example of a directive that converts field to lowercase |
| 92 | + |
| 93 | +```kotlin |
| 94 | +@GraphQLDirective(name = "lowercase", description = "Modifies the string field to lowercase") |
| 95 | +annotation class LowercaseDirective |
| 96 | + |
| 97 | +class LowercaseSchemaDirectiveWiring : KotlinSchemaDirectiveWiring { |
| 98 | + |
| 99 | + override fun onField(environment: KotlinFieldDirectiveEnvironment): GraphQLFieldDefinition { |
| 100 | + val field = environment.element |
| 101 | + val originalDataFetcher: DataFetcher<Any> = environment.getDataFetcher() |
| 102 | + |
| 103 | + val lowerCaseFetcher = DataFetcherFactories.wrapDataFetcher( |
| 104 | + originalDataFetcher, |
| 105 | + BiFunction<DataFetchingEnvironment, Any, Any>{ _, value -> value.toString().toLowerCase() } |
| 106 | + ) |
| 107 | + environment.setDataFetcher(lowerCaseFetcher) |
| 108 | + return field |
| 109 | + } |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +While you can manually apply all the runtime wirings to the corresponding GraphQL types directly in |
| 114 | +`SchemaGeneratorHooks#onRewireGraphQLType`, we recommend the usage of our |
| 115 | +[KotlinDirectiveWiringFactory](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/directives/KotlinDirectiveWiringFactory.kt) |
| 116 | +to simplify the integrations. `KotlinDirectiveWiringFactory` accepts a mapping of directives to corresponding wirings or |
| 117 | +could be extended to provide the wirings through `KotlinDirectiveWiringFactory#getSchemaDirectiveWiring` that accepts |
| 118 | +`KotlinSchemaDirectiveEnvironment`. |
| 119 | + |
| 120 | +```kotlin |
| 121 | +val queries = ... |
| 122 | +val customWiringFactory = KotlinDirectiveWiringFactory( |
| 123 | + manualWiring = mapOf<String, KotlinSchemaDirectiveWiring>("lowercase" to LowercaseSchemaDirectiveWiring())) |
| 124 | +val customHooks = object : SchemaGeneratorHooks { |
| 125 | + override val wiringFactory: KotlinDirectiveWiringFactory |
| 126 | + get() = customWiringFactory |
| 127 | +} |
| 128 | +val schemaGeneratorConfig = SchemaGeneratorConfig(hooks = customHooks) |
| 129 | +val schema = toSchema(queries = queries, config = schemaGeneratorConfig) |
| 130 | +``` |
| 131 | + |
| 132 | +While providing directives on different schema elements you will be able to modify the underlying GraphQL types. Keep in |
| 133 | +mind though that data fetchers are used to resolve the fields so only field directives (and by association their |
| 134 | +arguments directives) can modify runtime behavior based on the context and user input. |
| 135 | + |
| 136 | +**NOTE: `graphql-kotlin` prioritizes manual wiring mappings over the wirings provided by the |
| 137 | +`KotlinDirectiveWiringFactory#getSchemaDirectiveWiring`. This is a different behavior than `graphql-java` which will |
| 138 | +first attempt to use `WiringFactory` and then fallback to manual overrides.** |
| 139 | + |
| 140 | +For more details please refer to the example usage of directives in our [example |
| 141 | +app](https://github.com/ExpediaGroup/graphql-kotlin/tree/master/examples/spring). |
| 142 | + |
| 143 | +## Directive Chaining |
| 144 | + |
| 145 | +Directives are applied in the order annotations are declared on the given object. Given |
| 146 | + |
| 147 | +```kotlin |
| 148 | + @Directive1 |
| 149 | + @Directive2 |
| 150 | + fun doSomething(): String { |
| 151 | + // does something |
| 152 | + } |
| 153 | +``` |
| 154 | + |
| 155 | +`Directive1` will be applied first followed by the `Directive2`. |
0 commit comments