Ktor is an asynchronous HTTP client and server framework for Kotlin that supports multiplatform development. The library is built on Kotlin coroutines and runs on JVM, iOS, Android, JS, and Native. According to the Ktor repository on GitHub, the project is actively developed by the JetBrains team. Ktor offers a modular architecture with a plugin system for flexible HTTP connection configuration.
Key Takeaways
Ktor is a framework for creating HTTP clients and servers in Kotlin, developed by JetBrains. Unlike traditional libraries, Ktor was designed from the start for multiplatform development and works on all platforms supported by Kotlin.
Ktor uses a middleware handler approach, inspired by the architecture of Kodein and Express.js. Each request passes through a pipeline of handler functions that can modify the request and response. This provides flexibility not available in libraries with rigid annotation-based architecture.
The current version Ktor 3.0 includes support for Kotlin 2.0, the K2 compiler, and a new CIO (Coroutine I/O) engine with improved performance. The library is distributed under the Apache 2.0 license and is available for commercial use without restrictions.
The client side of Ktor is fully built on Kotlin coroutines, providing efficient asynchronous request execution without thread blocking. The server side allows creating HTTP servers with routing, request handling, and WebSocket connections.
Ktor uses a plugin architecture: all additional features — logging, serialization, authentication — are connected through plugins. This makes the library modular and allows connecting only the necessary components, reducing the final application size.
Thanks to a unified API across all platforms, developers don’t need to learn different HTTP clients for iOS and Android. In a multiplatform project, the network layer code is fully shared, and the platform-specific implementation is hidden behind the HttpClient engine. This reduces development time and decreases the number of errors related to platform differences.
Ktor provides a set of features that make it an attractive choice for modern Kotlin projects, especially multiplatform ones.
Ktor works on JVM, Android, iOS, macOS, Windows, Linux, JavaScript, and Wasm. The same HTTP client code runs on all platforms without changes. This is a key advantage over libraries tied to OkHttp or URLSession.
Coroutines in Kotlin provide natural asynchronicity without callbacks. Each request is a suspend function that can be called from any coroutine. Ktor supports response streaming via Flow, which is convenient for long connections and WebSocket.
Plugins in Ktor are connected through an install block and configured separately. Main plugins: ContentNegotiation for serialization, Logging for logging, Auth for authentication, and WebSockets for bidirectional communication. Each plugin can be enabled or disabled independently.
Error handling in Ktor is based on exceptions. The ClientRequestException class is thrown for 4xx codes, ServerResponseException for 5xx, and IOException for network failures. Timeouts are configured through the HttpTimeout plugin, which sets the timeout for connection, read, and write. For retry attempts, the Retry plugin is used with settings for the number of attempts and delay.
Ktor uses a pipeline architecture where each request passes through a chain of handlers. The client creates an HttpClient configuration with installed plugins, and each call to get or post passes through the plugins in the order they were connected.
The HttpClient object is created with an engine specific to the platform: CIO for JVM and Android, Darwin for iOS and macOS, OkHttp for Android compatibility, Js for browser. The engine can be explicitly selected or left to automatic choice. Each request returns an HttpResponse containing the response body, headers, and status.
val client = HttpClient(CIO) {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
})
}
}
suspend fun fetchUsers(): List<User> {
return client.get("https://api.example.com/users").body()
}
Installation of Ktor is done via Gradle or Maven. For multiplatform projects, dependencies are specified in sourceSets for each target. Ktor is distributed through Maven Central.
In build.gradle.kts, add the ktor-client-core dependency for common code and an engine for the specific platform. The Ktor version is set via a variable in gradle.properties. Ktor 3.x requires Kotlin 2.0+ and supports the K2 compiler.
val ktorVersion = "3.0.3"
dependencies {
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.ktor:ktor-client-logging:$ktorVersion")
}
For iOS, the Darwin engine is used, which wraps native URLSession. In Kotlin Multiplatform, this provides maximum performance and integration with iOS system caching mechanisms. The engine is added as a separate dependency in the iOS sourceSet.
An important feature of Ktor is support for different serialization formats through ContentNegotiation. In addition to JSON, the plugin supports Protobuf, CBOR, XML, and custom formats. For serialization, kotlinx.serialization or Jackson libraries are used, and the developer can switch between them without changing request code.
The examples below demonstrate typical scenarios for working with the Ktor client: a basic GET request, sending data, and working with multiplatform code.
A simple GET request with automatic deserialization of the response into a data class. Ktor uses the ContentNegotiation plugin with kotlinx.serialization to convert JSON into objects. The code is concise and type-safe.
@Serializable
data class Post(
val id: Int,
val title: String,
val body: String
)
suspend fun getPosts(): List<Post> {
val response = client.get("https://jsonplaceholder.typicode.com/posts")
return response.body()
}
A POST request in Ktor sends a data class as a JSON body via the post method with contentType and setBody. The ContentNegotiation plugin automatically serializes the object into a JSON string. The response can be processed synchronously or asynchronously.
suspend fun createPost(): Post {
val newPost = Post(
id = 0,
title = "New Post",
body = "Post Content"
)
val response = client.post("https://jsonplaceholder.typicode.com/posts") {
contentType(ContentType.Application.Json)
setBody(newPost)
}
return response.body()
}
The submitFormWithBinaryData method in Ktor allows sending files and forms in multipart format. Ktor automatically splits the data into parts and adds headers. To track progress, onUpload is used, which receives the bytes of sent data.
suspend fun uploadFile(fileBytes: ByteArray) {
client.submitFormWithBinaryData(
url = "https://api.example.com/upload",
formData = formData {
append("file", fileBytes, Headers.build {
append(HttpHeaders.ContentType, "image/png")
append(HttpHeaders.ContentDisposition, "filename=\"photo.png\"")
})
}
)
}
The choice between Ktor and Retrofit depends on the project architecture and multiplatform requirements. Retrofit remains the standard for Android-only projects, while Ktor is the best choice for Kotlin Multiplatform.
Ktor also provides built-in WebSocket and SSE (Server-Sent Events) support, making it convenient for real-time applications. Retrofit does not directly support WebSocket — a separate OkHttp WebSocket library is required. Ktor is also easier to configure for different environments thanks to its plugin system, where each plugin is responsible for one function.
The Auth plugin in Ktor supports basic authentication, Bearer tokens, Digest, and OAuth2. Authentication configuration is done declaratively: the developer specifies the provider, token source, and scope. Ktor automatically adds authentication headers to requests and can refresh the token when it expires.
If a project uses Kotlin Multiplatform with shared code on iOS and Android, Ktor is the only option that works on both platforms without additional layers. Retrofit is tightly tied to OkHttp and JVM, making it unsuitable for iOS.
For Android-only projects, Retrofit provides a more mature API, a larger number of converters and OkHttp interceptors. Ktor also works in this scenario, but its plugin ecosystem is less extensive. Both libraries support coroutines and provide comparable performance.
| Criterion | Ktor | Retrofit |
|---|---|---|
| Multiplatform | iOS, Android, JVM, JS, Native | JVM and Android only |
| HTTP Engine | CIO, Darwin, OkHttp, Js | OkHttp |
| Converters | kotlinx.serialization, Jackson | Gson, Moshi, Jackson, Protobuf |
| Architecture | Pipeline with plugins | Annotations with code generation |
| Developer | JetBrains | Square |
Frequently Asked Questions
Ktor is a multiplatform HTTP client on coroutines from JetBrains. Retrofit is an Android library from Square based on OkHttp. Ktor works on iOS, Android, JS, and Native, while Retrofit only works on JVM.
Yes, Ktor supports iOS through the Darwin engine, which uses native URLSession. This provides maximum performance and correct work with the iOS system cache. The client code remains shared between platforms.
Ktor supports engines: CIO (JVM/Android), Darwin (iOS/macOS), OkHttp (Android), Js (browser), Jetty, Netty, Tomcat (server). The engine can be explicitly selected or left to automatic default selection.
Yes, Ktor has built-in WebSocket support on both client and server. The WebSockets plugin is used for the client, allowing bidirectional connection and real-time message exchange.
Errors are handled via try-catch around suspend calls. Ktor throws ClientRequestException for 4xx, ServerResponseException for 5xx, and IOException for network errors. Using the Result type for unification is recommended.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also