Inheritance
When dealing with GraphQL schema containing field arguments, generating Kotlin code can be a bit tricky. This is because the generated code cannot in itself be the implementation in a resolver.
Here's an example:
type Query {
resolveMyType: MyType!
}
type MyType {
resolveMe(input: String!): String!
}
Generated Kotlin:
package com.types.generated
open class Query {
open fun resolveMyType(): MyType = throw NotImplementedError("Query.resolveMyType must be implemented.")
}
open class MyType {
open fun resolveMe(input: String): String = throw NotImplementedError("MyType.resolveMe must be implemented.")
}
Source code:
import com.expediagroup.graphql.server.operations.Query
import com.types.generated.MyType as MyTypeInterface
import com.types.generated.Query as QueryInterface
class MyQuery : Query, QueryInterface() {
override fun resolveMyType(): MyTypeInterface = MyType()
}
class MyType : MyTypeInterface() {
override fun resolveMe(input: String): String = "Hello world!"
}
As you can see, the generated code is not part of the implementation. Rather, it becomes an interface to inherit from in your implementation. This enforces a type contract between the schema and your resolver code.
Note that GraphQL Kotlin will use the implementation classes to generate the schema, not the generated interfaces.
This means that all @GraphQLDescription
and @Deprecated
annotations have to be added to implementation classes
in order to be propagated to the resulting schema.
Top Level Types
When dealing with top-level types, i.e. Query
and Mutation
, you can inherit from the corresponding generated class
to enforce the type contract. This is fine as long as all of your resolvers are contained in the same Query or Mutation class.
type Query {
foo: String!
bar: String!
}
import com.expediagroup.graphql.server.operations.Query
import com.types.generated.Query as QueryInterface
class MyQuery : Query, QueryInterface() {
override fun foo(): String = "Hello"
override fun bar(): String = "World"
}
However, you might want to separate the implementation into multiple classes like so:
import com.expediagroup.graphql.server.operations.Query
class FooQuery : Query {
override fun foo(): String = "Hello"
}
class BarQuery : Query {
override fun bar(): String = "World"
}
If you try to inherit from the generated Query
class, you will get an error during schema generation.
import com.expediagroup.graphql.server.operations.Query
import com.types.generated.Query as QueryInterface
class FooQuery : Query, QueryInterface() {
override fun foo(): String = "Hello"
}
class BarQuery : Query, QueryInterface() {
override fun bar(): String = "World"
}
This is because the generated Query
class contains both foo
and bar
fields, which creates a conflict when inherited by multiple implementation classes.
Instead, you should inherit from the field-level generated Query
classes like so:
import com.expediagroup.graphql.server.operations.Query
import com.types.generated.FooQueryInterface
import com.types.generated.BarQueryInterface
class FooQuery : Query, FooQueryInterface() {
override fun foo(): String = "Hello"
}
class BarQuery : Query, BarQueryInterface() {
override fun bar(): String = "World"
}
This way, schema generation can complete without conflict, and you can separate your implementation into multiple classes!