Skip to content

Commit c0be1c1

Browse files
committed
docs
1 parent 492fb70 commit c0be1c1

File tree

2 files changed

+131
-16
lines changed

2 files changed

+131
-16
lines changed

docs/docs/inheritance.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
sidebar_position: 6
3+
---
4+
5+
# Inheritance
6+
7+
When dealing with GraphQL schema containing [field arguments](https://graphql.com/learn/arguments/),
8+
generating Kotlin code can be a bit tricky. This is because the generated code cannot in itself be the implementation
9+
in a resolver.
10+
11+
Here's an example:
12+
13+
```graphql
14+
type Query {
15+
resolveMyType: MyType!
16+
}
17+
18+
type MyType {
19+
resolveMe(input: String!): String!
20+
}
21+
```
22+
23+
Generated Kotlin:
24+
25+
```kotlin
26+
package com.types.generated
27+
28+
open class Query {
29+
open fun resolveMyType(): MyType = throw NotImplementedError("Query.resolveMyType must be implemented.")
30+
}
31+
32+
open class MyType {
33+
open fun resolveMe(input: String): String = throw NotImplementedError("MyType.resolveMe must be implemented.")
34+
}
35+
```
36+
37+
Source code:
38+
39+
```kotlin
40+
import com.expediagroup.graphql.generator.annotations.GraphQLIgnore
41+
import com.expediagroup.graphql.server.operations.Query
42+
import com.types.generated.MyType as MyTypeInterface
43+
import com.types.generated.Query as QueryInterface
44+
45+
class MyQuery : Query, QueryInterface() {
46+
override fun resolveMyType(): MyTypeInterface = MyType()
47+
}
48+
49+
@GraphQLIgnore
50+
class MyType : MyTypeInterface() {
51+
override fun resolveMe(input: String): String = "Hello world!"
52+
}
53+
```
54+
55+
As you can see, the generated code is not part of the implementation. Rather, it becomes an interface to inherit from in your implementation.
56+
This enforces a type contract between the schema and your resolver code.
57+
58+
## Top Level Types
59+
60+
When dealing with top-level types, such as `Query` or `Mutation`, you can inherit from the generated class directly. This is because the generated class is open by default.
61+
62+
Given the following executable schema:
63+
64+
```graphql
65+
type Query {
66+
foo: String!
67+
bar: String!
68+
}
69+
```
70+
71+
You might want to separate the implementation into multiple classes like so:
72+
73+
```kotlin
74+
import com.expediagroup.graphql.server.operations.Query
75+
76+
class FooQuery : Query {
77+
override fun foo(): String = "Hello"
78+
}
79+
80+
class BarQuery : Query {
81+
override fun bar(): String = "World"
82+
}
83+
```
84+
85+
However, if you try to inherit from the generated `Query` class, you will get an error during schema generation.
86+
87+
```kotlin
88+
import com.expediagroup.graphql.server.operations.Query
89+
import com.types.generated.Query as QueryInterface
90+
91+
class FooQuery : Query, QueryInterface() {
92+
override fun foo(): String = "Hello"
93+
}
94+
95+
class BarQuery : Query, QueryInterface() {
96+
override fun bar(): String = "World"
97+
}
98+
```
99+
100+
This is because the generated `Query` class contains both `foo` and `bar` fields, which causes a conflict when inherited by multiple implementation classes.
101+
102+
Instead, you should inherit from the field-level generated `Query` classes like so:
103+
104+
```kotlin
105+
import com.expediagroup.graphql.server.operations.Query
106+
import com.types.generated.FooQuery as FooQueryInterface
107+
import com.types.generated.BarQuery as BarQueryInterface
108+
109+
class FooQuery : Query, FooQueryInterface() {
110+
override fun foo(): String = "Hello"
111+
}
112+
113+
class BarQuery : Query, BarQueryInterface() {
114+
override fun bar(): String = "World"
115+
}
116+
```

docs/docs/recommended-usage.md

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ type Query {
2121
}
2222

2323
type MyType {
24-
field1: String!
25-
field2: String
24+
foo: String!
25+
bar: String
2626
}
2727
```
2828

@@ -38,8 +38,8 @@ open class Query {
3838
}
3939

4040
data class MyType(
41-
val field1: String,
42-
val field2: String? = null
41+
val foo: String,
42+
val bar: String? = null
4343
)
4444
```
4545

@@ -53,16 +53,15 @@ import com.types.generated.Query as QueryInterface
5353
class MyQuery : Query, QueryInterface() {
5454
override fun resolveMyType(input: String): MyType =
5555
MyType(
56-
field1 = myExpensiveCall1(),
57-
field2 = myExpensiveCall2()
56+
foo = myExpensiveCall1(),
57+
bar = myExpensiveCall2()
5858
)
5959
}
60-
6160
```
6261

6362
The resulting source code is extremely unperformant. The `MyType` class is a data class, which means
64-
that the `field1` and `field2` properties are both initialized when the `MyType` object is created, and
65-
`myExpensiveCall1()` and `myExpensiveCall2()` will both be called in sequence! Even if I only query for `field1`, not
63+
that the `foo` and `bar` properties are both initialized when the `MyType` object is created, and
64+
`myExpensiveCall1()` and `myExpensiveCall2()` will both be called in sequence! Even if I only query for `foo`, not
6665
only will `myExpensiveCall2()` still run, but it will also wait until `myExpensiveCall1()` is totally finished.
6766

6867
### Instead, use the `resolverInterfaces` config!
@@ -91,8 +90,8 @@ open class Query {
9190
}
9291

9392
open class MyType {
94-
open fun field1(): String = throw NotImplementedError("MyType.field1 must be implemented.")
95-
open fun field2(): String? = throw NotImplementedError("MyType.field2 must be implemented.")
93+
open fun foo(): String = throw NotImplementedError("MyType.foo must be implemented.")
94+
open fun bar(): String? = throw NotImplementedError("MyType.bar must be implemented.")
9695
}
9796
```
9897

@@ -108,13 +107,13 @@ class MyQuery : Query, QueryInterface() {
108107

109108
@GraphQLIgnore
110109
class MyType : MyTypeInterface() {
111-
override fun field1(): String = myExpensiveCall1()
112-
override fun field2(): String? = myExpensiveCall2()
110+
override fun foo(): String = myExpensiveCall1()
111+
override fun bar(): String? = myExpensiveCall2()
113112
}
114113
```
115114

116-
This code is much more performant! The `MyType` class is no longer a data class, so the `field1` and `field2` properties
117-
can now be resolved independently of each other. If I query for only `field1`, only `myExpensiveCall1()` will be called, and
118-
if I query for only `field2`, only `myExpensiveCall2()` will be called.
115+
This code is much more performant! The `MyType` class is no longer a data class, so the `foo` and `bar` properties
116+
can now be resolved independently of each other. If I query for only `foo`, only `myExpensiveCall1()` will be called, and
117+
if I query for only `bar`, only `myExpensiveCall2()` will be called.
119118

120119
Check out the [related GraphQL Kotlin docs](https://opensource.expediagroup.com/graphql-kotlin/docs/schema-generator/execution/fetching-data/) for more information on this topic.

0 commit comments

Comments
 (0)