Skip to content

Commit 999c555

Browse files
authored
Add docs for new schema generator feature (#594)
Added new features in #593 that we did not document
1 parent cb7ba81 commit 999c555

File tree

5 files changed

+192
-159
lines changed

5 files changed

+192
-159
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
id: advanced-features
3+
title: Advanced Features
4+
---
5+
6+
## Adding Custom Additional Types
7+
8+
There are a couple ways you can add more types to the schema without having them be directly consumed by a type in your schema.
9+
This may be required for [Apollo Federation](federated/apollo-federation), or maybe adding other interface implementations that are not picked up.
10+
11+
12+
### `SchemaGenerator::addAdditionalTypesWithAnnotation`
13+
14+
This method is protected so if you override the `SchemaGenerator` used you can call this method to add types that have a specific annotation.
15+
You can see how this is used in `graphql-kotlin-federation` as [an example](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-federation/src/main/kotlin/com/expediagroup/graphql/federation/FederatedSchemaGenerator.kt).
16+
17+
### `SchemaGenerator::generateAdditionalTypes`
18+
19+
This method is called by `SchemaGenerator::get` after all the queries, mutations, and subscriptions have been generated and it is going to add all the types saved in an internal set that were not generated by reflection.
20+
To change the behaviour, you can update the set and then call the super method with the new value.
21+
22+
Example:
23+
24+
```kotlin
25+
class CustomSchemaGenerator(config: SchemaGeneratorConfig) : SchemaGenerator(config) {
26+
27+
override fun generateAdditionalTypes(types: Set<KType>): Set<GraphQLType> {
28+
val newTypes = types.toMutableSet().add(MyNewType()::class.createType())
29+
return super.generateAdditionalTypes(newTypes)
30+
}
31+
}
32+
```

docs/customizing-schemas/evolving-schema.md renamed to docs/customizing-schemas/deprecating-schema.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
id: evolving-schema
3-
title: Evolving Schema
2+
id: deprecating-schema
3+
title: Deprecating Schema
44
---
55

66
### Deprecating Fields
Lines changed: 155 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,155 +1,155 @@
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`.

docs/writing-schemas/annotations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ for things that can't be directly derived from Kotlin reflection.
1212
* [@GraphQLID](scalars#id) - Marks given field as GraphQL `ID`
1313
* [@GraphQLIgnore](../customizing-schemas/excluding-fields) - Exclude field from the GraphQL schema
1414
* [@GraphQLName](../customizing-schemas/renaming-fields) - Override the name used for the type
15-
* Kotlin built in [@Deprecated](../customizing-schemas/evolving-schema) - Apply the GraphQL `@deprecated` directive on the field
15+
* Kotlin built in [@Deprecated](../customizing-schemas/deprecating-schema) - Apply the GraphQL `@deprecated` directive on the field

website/sidebars.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"customizing-schemas/excluding-fields",
3131
"customizing-schemas/renaming-fields",
3232
"customizing-schemas/directives",
33-
"customizing-schemas/evolving-schema"
33+
"customizing-schemas/deprecating-schema",
34+
"customizing-schemas/advanced-features"
3435
]
3536
},
3637
{

0 commit comments

Comments
 (0)