EXPEDIA GROUP TECHNOLOGY — SOFTWARE

Introducing GraphQL Kotlin Client

New lightweight, type-safe GraphQL HTTP Client

Dariusz Kuc
Expedia Group Technology

--

View of forest from inside tent, the tent’s opening has triangular and circular layers which resemble the GraphQL logo

HTTP is the most common way of interacting with a GraphQL server. No special clients required as you simply send an HTTP POST request with a query field in a JSON body. While this approach can certainly work, it is frail and can easily blow up. There are a number of advanced clients available for different languages but so far there was no Kotlin native solution.

graphql-kotlin-client is a new lightweight, type-safe GraphQL HTTP client from Expedia Group™️. Gradle and Maven plugins for GraphQL Kotlin generate Kotlin data classes based on your queries and validate them against a target GraphQL schema. graphql-kotlin-client is highly customizable as it utilizes Ktor HTTP Client to communicate asynchronously with GraphQL servers and supports configuration to use any of the supported Ktor HTTP engines and features.

Project Configuration

GraphQL Kotlin provides both Gradle and Maven plugins to automatically generate your client code at build time. Once you’ve generated data classes, execute their underlying GraphQL operations using the graphql-kotlin-client runtime dependency.

Basic build.gradle.kts Gradle configuration

Equivalent Maven pom.xml configuration

See graphql-kotlin-client-example project for complete working examples of Gradle and Maven based projects.

line separator

Generating GraphQL Client

By default, GraphQL Kotlin build plugins generate GraphQL clients from all .graphql files located under src/main/resources. They also validate queries against target GraphQL schema.

Schema can be manually provided, retrieved by the plugins through introspection (as configured in examples above) or downloaded directly from a custom SDL endpoint. See our documentation for more details on supported Gradle tasks and Maven Mojos.

When creating GraphQL queries make sure to always specify an operation name and name the files accordingly. Each one of your query files will generate a corresponding Kotlin file with a class matching your operation name that will act as a wrapper for all corresponding data classes. For example, given HelloWorldQuery.graphql with HelloWorldQuery as the operation name, GraphQL Kotlin plugins will generate a corresponding HelloWorldQuery.kt file with a HelloWorldQuery class under the configured package.

For example, given a simple schema

And a corresponding HelloWorldQuery.graphql query

Plugins will generate following client code

Generated classes requires an instance of GraphQLClient and expose a single execute suspendable method that executes the underlying GraphQL operation using the provided client.

Executing Queries

Your auto generated classes accept an instance of GraphQLClient which is a thin wrapper around Ktor HTTP client that ensures proper serialization and deserialization of your GraphQL objects. GraphQLClient requires target URL to be specified and defaults to fully asynchronous non-blocking Coroutine-based IO engine.

Client Features

Polymorphic Types Support

GraphQL supports polymorphic types through unions and interfaces which Kotlin represents as marker and regular interfaces. To ensure generated objects are not empty, GraphQL queries referencing polymorphic types have to explicitly specify all implementations.

Polymorphic queries also have to explicitly request __typename field. Jackson uses it to correctly distinguish between different implementations.

Given example schema

We can query interface field as

Which will generate following data model

Default Enum Values

Enums represent predefined sets of values. Adding additional enum values could be a potentially breaking change as your clients may not be able to process the additional values. GraphQL Kotlin Client automatically adds default @JsonEnumDefaultValue __UNKNOWN_VALUE to all generated enums as a catch all safeguard for handling new enum values.

Auto Generated Documentation

GraphQL Kotlin build plugins automatically pull in GraphQL descriptions of the queried fields from the target schema and add it as KDoc to corresponding data models.

Given simple GraphQL object definition

Will result in a corresponding auto generated data class

Native Support for Coroutines

GraphQL Kotlin Client is a thin wrapper on top of Ktor HTTP Client which provides fully asynchronous communication through Kotlin coroutines. GraphQLClient exposes a single execute method that will
suspend your GraphQL operation until it gets a response without blocking the underlying thread. In order to fetch data asynchronously and perform additional computations at the same time you should wrap your client execution in launch or async coroutine builder and explicitly await for their results.

See Kotlin coroutines documentation for additional details.

line separator

Client Customization

Ktor HTTP Client Customization

GraphQLClient uses the Ktor HTTP Client to execute underlying queries. Clients can be customized with different engines (defaults to Coroutine-based IO) and HTTP client features. Ktor DSL style builders can apply custom configurations.

See Ktor HTTP Client documentation for details.

Jackson Customization

GraphQLClient relies on Jackson to handle polymorphic types and default enum values. Due to the necessary logic to handle the above, currently we don’t support other JSON libraries.

Deprecated Field Usage

Build plugins automatically fail generation of a client if any of the specified query files are referencing deprecated fields. This ensures that your clients have to explicitly opt-in into deprecated usage by specifying the allowDeprecatedFields configuration option.

Custom GraphQL Scalars

By default, custom GraphQL scalars are serialized and type-aliased to aString. GraphQL Kotlin plugins also support custom serialization based on provided configuration.

In order to automatically convert between custom GraphQL UUID scalar type and java.util.UUID, we first need to create our custom ScalarConverter.

And then configure build plugin by specifying

  • Custom GraphQL scalar name
  • Target class name
  • Converter that provides logic to map between GraphQL and Kotlin type

See Gradle and Maven plugin documentation for details.

Further Reading

GraphQL Kotlin Client is a new Kotlin lightweight type-safe GraphQL HTTP client that leverages the power of Kotlin coroutines for fully asynchronous communications. Type-safe data models are conveniently generated at build time by the provided GraphQL Kotlin Gradle and Maven plugins. For additional details on how to use the library please check out the documentation available on the GitHub pages.

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

--

--