HTTP Client in Kotlin: Making Requests Properly — Advanced Level

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

HTTP Client in Kotlin: Making Requests Properly — Advanced Level



When developing modern applications in Kotlin, working with HTTP requests goes far beyond simply calling URL.readText(). At an advanced level, it is necessary to consider connection management, timeouts, retries, logging, asynchrony, and proper error handling. In this article, we will look at how to configure an HTTP client in Kotlin for a production environment using two popular libraries: OkHttp and Ktor Client. The material is intended for developers already familiar with the basics of Kotlin and HTTP.



1. Choosing a Library: OkHttp vs Ktor Client

Before writing code, it is important to understand the difference between the approaches. OkHttp is a mature, time-tested library from Square that works great in conjunction with Retrofit. Ktor Client is a native multiplatform library from JetBrains, fully integrated with Kotlin coroutines. For server projects on Spring Boot, OkHttp is often chosen, while for Ktor applications or multiplatform projects, Ktor Client is preferred.



We will look at both options so you can choose the one suitable for your stack.



2. Advanced OkHttp Configuration

Basic OkHttp configuration is trivial, but production requires fine-tuning. Let's look at creating a Singleton client with custom parameters.



import okhttp3.*import java.util.concurrent.TimeUnit

object HttpClientProvider {

private val client: OkHttpClient by lazy { OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .addInterceptor(LoggingInterceptor()) .addInterceptor(RetryInterceptor(maxRetries = 3)) .connectionPool(ConnectionPool(5, 5, TimeUnit.MINUTES)) .build() }

fun getClient(): OkHttpClient = client}


2.1 Custom Interceptors

Interceptors are a powerful mechanism for modifying requests and responses. Below is an example of a logging interceptor and a retry interceptor.



class LoggingInterceptor : Interceptor {    override fun intercept(chain: Interceptor.Chain): Response {        val request = chain.request()        println("--> ${request.method} ${request.url}")        val startTime = System.nanoTime()        val response = chain.proceed(request)        val duration = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)        println("<-- ${response.code} ${response.message} (${duration}ms)")        return response    }}

class RetryInterceptor(private val maxRetries: Int) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { var retryCount = 0 var response: Response? = null while (retryCount < maxRetries) { try { response = chain.proceed(chain.request()) if (response.isSuccessful) return response } catch (e: Exception) { println("Retry #${retryCount + 1} failed: ${e.message}") } retryCount++ if (retryCount < maxRetries) Thread.sleep(1000L * retryCount) } return response ?: throw IOException("Max retries exceeded") }}


2.2 Asynchronous Requests with Coroutines

OkHttp supports synchronous and asynchronous (callback-based) requests. To integrate with Kotlin coroutines, we use suspendCancellableCoroutine.



suspend fun makeRequest(url: String): String {    return suspendCancellableCoroutine { continuation ->        val request = Request.Builder().url(url).build()        val call = HttpClientProvider.getClient().newC                

Blogs

Book Recommendations