Creating GraphQL Schemas in Kotlin

Generate Schemas Directly From Code

Dariusz Kuc
Expedia Group Technology
4 min readNov 5, 2019

--

Photo by Josue Isai Ramos Figueroa on Unsplash

Schemas play a central role in the GraphQL world. Traditionally, GraphQL services are developed following a schema-first approach, where you first manually define your schema using Schema Definition Language (SDL) and then map it to corresponding resolvers and types. While this approach can work and is still very popular, there are a number of issues with it. A code-first, or resolver-first, approach is an alternative where the schema is generated directly from code.

GraphQL Kotlin consists of a number of libraries that aim to simplify GraphQL integration for Kotlin applications. Due to similarities between Kotlin and GraphQL, including strong type systems and their ability to define nullable types, Kotlin code can generate a schema directly without a separate specification. With graphql-kotlin-schema-generator developers simply write their functions as they would when implementing traditional REST-based APIs and the resulting code automatically maps to corresponding GraphQL queries and types.

The Basics

graphql-kotlin-schema-generator provides a single function, toSchema, to generate a schema from Kotlin objects. This function accepts four arguments: config, queries, mutations and subscriptions. The queries, mutations and subscriptions are a list of objects that will generate corresponding GraphQL root types. The config contains all the extra information needed to generate the schema, including supported packages, custom hooks and name overrides.

The example above generates the following GraphQL schema:

Documenting Schemas

graphql-kotlin-schema-generator utilizes reflections to generate corresponding GraphQL schemas from Kotlin source code. While Kotlin reflections are powerful they impose limitations such as lack of access to KDocs, Kotlin’s equivalent of Javadocs. In order to document your schema elements, you need to explicitly provide the documentation through @GraphQLDescription annotations.

GraphQL directives can transform schemas as well as modify the runtime behavior of a query. The deprecated directive is one of the built-in directives and can be applied to a target field by simply annotating it with Kotlin @Deprecated annotation.

Applying those annotations on our original example, we end up with the following code:

This generates the following schema:

Contextual Data

All GraphQL servers have a concept of a “context”. A GraphQL context contains metadata that is useful to the GraphQL server but shouldn’t necessarily be part of the GraphQL schema.

A prime example of something appropriate for the GraphQL context would be trace headers for an OpenTracing system such as Haystack. The GraphQL query does not need the information to perform its function, but the server needs the information to ensure observability.

Once a GraphQL application is configured to build a custom MyGraphQLContext, simply add an @GraphQLContext annotation to any function argument and the corresponding GraphQL context from the environment will be automatically injected during execution.

The above results in the following schema (notice the absence of context argument and type):

Collections

Both kotlin.Array and kotlin.collections.List automatically map to the GraphQL List type. Type arguments provided to Kotlin collections are mapped to the type arguments in the GraphQL List type. graphql-kotlin-schema-generator also supports Kotlin specialized classes representing arrays of Java primitive types without boxing overhead (e.g. IntArray).

The above generates the following queries:

Polymorphic Types

GraphQL supports interfaces and union polymorphic types but only a single level of inheritance is supported (see RFC373). The interface abstract type defines a common set of fields that are shared across all implementing types. Unions are similar to the interfaces but they do not share any common fields.

graphql-kotlin-schema-generator will generate the following schema:

Generating Resolvers

While generating the schema, graphql-kotlin-schema-generator automatically creates resolvers (aka data fetchers) for the underlying functions exposed in our schema. By default all functions are resolved synchronously, i.e. execution will block the underlying thread while executing the target function.

While you could configure your GraphQL server with execution strategies that execute each query in parallel on a thread pool, we highly recommend utilizing asynchronous programming models such as Kotlin coroutines or Java CompletableFuture.

The example above results in the following queries:

Support for additional monad types, such as Single from RxJava or Mono from Project Reactor, can be added through schema generation hooks.

Further Reading

GraphQL Kotlin consists of a number of libraries that aim to simplify the creation of a GraphQL server in Kotlin. graphql-kotlin-schema-generator allows developers to generate their GraphQL schemas directly from their code. The schema generation process is pretty flexible and can be customized by providing various hooks that fire during specific schema generation lifecycle events. For additional details on how to use the library please check out the examples and the documentation available on the GitHub pages. Stay tuned for additional blog posts where we’ll explore other GraphQL-Kotlin libraries.

We are also open for feedback so please raise an issue and start a discussion for any new features you would like to see!

Photo by Josue Isai Ramos Figueroa on Unsplash

--

--