Skip to main content
Version: 5.x.x

Client Customization

Ktor HTTP Client Customization#

GraphQLKtorClient is a thin wrapper on top of Ktor HTTP Client and supports fully asynchronous non-blocking communication. It is highly customizable and can be configured with any supported Ktor HTTP engine and features.

See Ktor HTTP Client documentation for additional details.

Global Client Customization#

A single instance of GraphQLKtorClient can be used to handle many GraphQL operations. You can specify a custom instance of Ktor HttpClient and a target GraphQLClientSerializer.

The below example configures a new GraphQLKtorClient to use the OkHttp engine with custom timeouts, adds a default X-MY-API-KEY header to all requests, and enables basic logging of the requests.

val okHttpClient = HttpClient(engineFactory = OkHttp) {    engine {        config {            connectTimeout(10, TimeUnit.SECONDS)            readTimeout(60, TimeUnit.SECONDS)            writeTimeout(60, TimeUnit.SECONDS)        }    }    defaultRequest {        header("X-MY-API-KEY", "someSecretApiKey")    }    install(Logging) {        logger = Logger.DEFAULT        level = LogLevel.INFO    }}val client = GraphQLKtorClient(    url = URL("http://localhost:8080/graphql"),    httpClient = okHttpClient)

Per Request Customization#

Individual GraphQL requests can be customized through HttpRequestBuilder. You can use this mechanism to specify custom headers, update target url to include custom query parameters, configure attributes that can be accessed from the pipeline features as well specify timeouts per request.

val helloWorldQuery = HelloWorldQuery(variables = HelloWorldQuery.Variables(name = "John Doe"))val result = client.execute(helloWorldQuery) {    header("X-B3-TraceId", "0123456789abcdef")}

Spring WebClient Customization#

GraphQLWebClient is a thin wrapper on top of Spring WebClient that relies on Reactor Netty for fully asynchronous non-blocking communications. If you want to use Jetty instead you will need to exclude provided io.projectreactor.netty:reactor-netty dependency and instead add org.eclipse.jetty:jetty-reactive-httpclient dependency.

Global Client Customization#

A single instance of GraphQLWebClient can be used to handle many GraphQL operations and you can customize it by providing a custom instance of WebClient.Builder. See Spring documentation for additional details.

Example below configures GraphQLWebClient with custom timeouts and adds a default X-MY-API-KEY header to all requests.

val httpClient: HttpClient = HttpClient.create()    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000)    .responseTimeout(Duration.ofMillis(10_000))val connector: ClientHttpConnector = ReactorClientHttpConnector(httpClient.wiretap(true))val webClientBuilder = WebClient.builder()    .clientConnector(connector)    .defaultHeader("X-MY-API-KEY", "someSecretApiKey")
val client = GraphQLWebClient(    url = "http://localhost:8080/graphql",    builder = webClientBuilder)

Per Request Customization#

Individual GraphQL requests can be customized by providing WebClient.RequestBodyUriSpec lambda. You can use this mechanism to specify custom headers or include custom attributes or query parameters.

val helloWorldQuery = HelloWorldQuery(variables = HelloWorldQuery.Variables(name = "John Doe"))val result = client.execute(helloWorldQuery) {    header("X-B3-TraceId", "0123456789abcdef")}

Custom GraphQL Client#

GraphQL Kotlin libraries provide generic a GraphQLClient interface as well as Ktor HTTP Client and Spring WebClient based reference implementations. Both GraphQLKtorClient and GraphQLWebClient are open classes which means you can also extend them to provide some custom execute logic.

class CustomGraphQLClient(url: URL) : GraphQLKtorClient(url = url) {
    override suspend fun <T: Any> execute(request: GraphQLClientRequest<T>, requestCustomizer: HttpRequestBuilder.() -> Unit): GraphQLClientResponse<T> {        // custom init logic        val result = super.execute(request, requestCustomizer)        // custom finalize logic        return result    }}

Deprecated Field Usage#

Build plugins will 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 allowDeprecatedFields configuration option.

Custom GraphQL Scalars#

By default, custom GraphQL scalars are serialized and type-aliased to a String. 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.

package com.example.client
import com.expediagroup.graphql.client.converter.ScalarConverterimport java.util.UUID
class UUIDScalarConverter : ScalarConverter<UUID> {    override fun toScalar(rawValue: Any): UUID = UUID.fromString(rawValue.toString())    override fun toJson(value: UUID): Any = value.toString()}

And then configure build plugin by specifying

  • Custom GraphQL scalar name
  • Target JVM class name
  • Converter that provides logic to map between GraphQL and Kotlin type
graphql {    packageName = "com.example.generated"    endpoint = "http://localhost:8080/graphql"    customScalars = listOf(GraphQLScalar("UUID", "java.util.UUID", "com.example.UUIDScalarConverter"))}

Custom scalar fields will then be automatically converted to a java.util.UUID type using appropriate converter/serializer.

Following converters will be generated under com.example.generated.scalars package.

@Generatedpublic class AnyToUUIDConverter : StdConverter<Any, UUID>() {  private val converter: UUIDScalarConverter = UUIDScalarConverter()
  public override fun convert(`value`: Any): UUID = converter.toScalar(value)}
@Generatedpublic class UUIDToAnyConverter : StdConverter<UUID, Any>() {  private val converter: UUIDScalarConverter = UUIDScalarConverter()
  public override fun convert(`value`: UUID): Any = converter.toJson(value)}

Custom scalars fields will then be annotated with Jackson annotations referencing the above converters.

@Generatedpublic data class Result(  @JsonSerialize(converter = UUIDToAnyConverter::class)  @JsonDeserialize(converter = AnyToUUIDConverter::class)  public val custom: UUID,  @JsonSerialize(contentConverter = UUIDToAnyConverter::class)  @JsonDeserialize(contentConverter = AnyToUUIDConverter::class)  public val customList: List<UUID>)

See Gradle and Maven plugin documentation for additional details.