|
| 1 | +--- |
| 2 | +id: client-customization |
| 3 | +title: Client Customization |
| 4 | +--- |
| 5 | + |
| 6 | + |
| 7 | +import Tabs from '@theme/Tabs'; |
| 8 | +import TabItem from '@theme/TabItem'; |
| 9 | + |
| 10 | +## Ktor HTTP Client Customization |
| 11 | + |
| 12 | +`GraphQLKtorClient` is a thin wrapper on top of [Ktor HTTP Client](https://ktor.io/docs/client.html) and supports fully |
| 13 | +asynchronous non-blocking communication. It is highly customizable and can be configured with any supported Ktor HTTP |
| 14 | +[engine](https://ktor.io/clients/http-client/engines.html) and [features](https://ktor.io/clients/http-client/features.html). |
| 15 | + |
| 16 | +See [Ktor HTTP Client documentation](https://ktor.io/clients/index.html) for additional details. |
| 17 | + |
| 18 | +### Global Client Customization |
| 19 | + |
| 20 | +A single instance of `GraphQLKtorClient` can be used to handle many GraphQL operations. You can specify a custom instance |
| 21 | +of Ktor `HttpClient` and a target `GraphQLClientSerializer`. |
| 22 | + |
| 23 | +The below example configures a new `GraphQLKtorClient` to use the `OkHttp` engine with custom timeouts, adds a default `X-MY-API-KEY` |
| 24 | +header to all requests, and enables basic logging of the requests. |
| 25 | + |
| 26 | +```kotlin |
| 27 | +val okHttpClient = HttpClient(engineFactory = OkHttp) { |
| 28 | + engine { |
| 29 | + config { |
| 30 | + connectTimeout(10, TimeUnit.SECONDS) |
| 31 | + readTimeout(60, TimeUnit.SECONDS) |
| 32 | + writeTimeout(60, TimeUnit.SECONDS) |
| 33 | + } |
| 34 | + } |
| 35 | + defaultRequest { |
| 36 | + header("X-MY-API-KEY", "someSecretApiKey") |
| 37 | + } |
| 38 | + install(Logging) { |
| 39 | + logger = Logger.DEFAULT |
| 40 | + level = LogLevel.INFO |
| 41 | + } |
| 42 | +} |
| 43 | +val client = GraphQLKtorClient( |
| 44 | + url = URL("http://localhost:8080/graphql"), |
| 45 | + httpClient = okHttpClient |
| 46 | +) |
| 47 | +``` |
| 48 | + |
| 49 | +### Per Request Customization |
| 50 | + |
| 51 | +Individual GraphQL requests can be customized through [HttpRequestBuilder](https://ktor.io/docs/request.html#customizing-requests). |
| 52 | +You can use this mechanism to specify custom headers, update target url to include custom query parameters, configure |
| 53 | +attributes that can be accessed from the pipeline features as well specify timeouts per request. |
| 54 | + |
| 55 | +```kotlin |
| 56 | +val helloWorldQuery = HelloWorldQuery(variables = HelloWorldQuery.Variables(name = "John Doe")) |
| 57 | +val result = client.execute(helloWorldQuery) { |
| 58 | + header("X-B3-TraceId", "0123456789abcdef") |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +## Spring WebClient Customization |
| 63 | + |
| 64 | +`GraphQLWebClient` is a thin wrapper on top of [Spring WebClient](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html) |
| 65 | +that relies on Reactor Netty for fully asynchronous non-blocking communications. If you want to use Jetty instead you will |
| 66 | +need to exclude provided `io.projectreactor.netty:reactor-netty` dependency and instead add `org.eclipse.jetty:jetty-reactive-httpclient` |
| 67 | +dependency. |
| 68 | + |
| 69 | +### Global Client Customization |
| 70 | + |
| 71 | +A single instance of `GraphQLWebClient` can be used to handle many GraphQL operations and you can customize it by providing |
| 72 | +a custom instance of `WebClient.Builder`. See [Spring documentation](https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-webclient-customization) |
| 73 | +for additional details. |
| 74 | + |
| 75 | +Example below configures `GraphQLWebClient` with custom timeouts and adds a default `X-MY-API-KEY` header to all requests. |
| 76 | + |
| 77 | +```kotlin |
| 78 | +val httpClient: HttpClient = HttpClient.create() |
| 79 | + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10_000) |
| 80 | + .responseTimeout(Duration.ofMillis(10_000)) |
| 81 | +val connector: ClientHttpConnector = ReactorClientHttpConnector(httpClient.wiretap(true)) |
| 82 | +val webClientBuilder = WebClient.builder() |
| 83 | + .clientConnector(connector) |
| 84 | + .defaultHeader("X-MY-API-KEY", "someSecretApiKey") |
| 85 | + |
| 86 | +val client = GraphQLWebClient( |
| 87 | + url = "http://localhost:8080/graphql", |
| 88 | + builder = webClientBuilder |
| 89 | +) |
| 90 | +``` |
| 91 | + |
| 92 | +### Per Request Customization |
| 93 | + |
| 94 | +Individual GraphQL requests can be customized by providing `WebClient.RequestBodyUriSpec` lambda. You can use this mechanism |
| 95 | +to specify custom headers or include custom attributes or query parameters. |
| 96 | + |
| 97 | +```kotlin |
| 98 | +val helloWorldQuery = HelloWorldQuery(variables = HelloWorldQuery.Variables(name = "John Doe")) |
| 99 | +val result = client.execute(helloWorldQuery) { |
| 100 | + header("X-B3-TraceId", "0123456789abcdef") |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +## Custom GraphQL Client |
| 105 | + |
| 106 | +GraphQL Kotlin libraries provide generic a `GraphQLClient` interface as well as Ktor HTTP Client and Spring WebClient based |
| 107 | +reference implementations. Both `GraphQLKtorClient` and `GraphQLWebClient` are open classes which means you can also |
| 108 | +extend them to provide some custom `execute` logic. |
| 109 | + |
| 110 | +```kotlin |
| 111 | +class CustomGraphQLClient(url: URL) : GraphQLKtorClient(url = url) { |
| 112 | + |
| 113 | + override suspend fun <T: Any> execute(request: GraphQLClientRequest<T>, requestCustomizer: HttpRequestBuilder.() -> Unit): GraphQLClientResponse<T> { |
| 114 | + // custom init logic |
| 115 | + val result = super.execute(request, requestCustomizer) |
| 116 | + // custom finalize logic |
| 117 | + return result |
| 118 | + } |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +## Deprecated Field Usage |
| 123 | + |
| 124 | +Build plugins will automatically fail generation of a client if any of the specified query files are referencing |
| 125 | +deprecated fields. This ensures that your clients have to explicitly opt-in into deprecated usage by specifying |
| 126 | +`allowDeprecatedFields` configuration option. |
0 commit comments