Ktor to asynchroniczny klient HTTP dla Kotlina, opracowany przez firmę JetBrains jako część frameworku o tej samej nazwie do programowania serwerowego i klienckiego. Ktor jest zbudowany na korutynach Kotlina i obsługuje wieloplatformowość. Według danych JetBrains, 2025, Ktor zapewnia natywną integrację z ekosystemem Kotlina bez refleksji i dodatkowych zależności.
Najważniejsze
Ktor to framework do budowania asynchronicznych aplikacji serwerowych i klienckich w Kotlinie, stworzony przez firmę JetBrains. Ktor Client — część kliencka frameworku, udostępniająca klienta HTTP z pełną obsługą korutyn Kotlina, wieloplatformowością (JVM, Native, JS) i modułową architekturą opartą na pluginach.
Ktor pojawił się w 2018 roku jako alternatywa dla Retrofit i OkHttp w projektach Kotlin-first. W przeciwieństwie do Retrofit, który portował podejście Javy z adnotacjami, Ktor Client używa Kotlin DSL do konfiguracji zapytań — bez adnotacji i refleksji. Dzięki temu kod jest bardziej czytelny i bezpieczny typowo dla programistów Kotlina.
Według ankiety Kotlin Multiplatform 2024, Ktor Client jest używany w 35% projektów Kotlin Multiplatform Mobile (KMM), co czyni go drugim najpopularniejszym klientem HTTP po OkHttp w społeczności Kotlina. Ktor jest preferowany w projektach, gdzie ważna jest wieloplatformowość i natywna integracja z ekosystemem Kotlina.
Architektura Ktor Client opiera się na potoku (pipeline) z pluginów. Każde żądanie przechodzi przez sekwencję zainstalowanych pluginów, które mogą modyfikować żądanie, odpowiedź lub wykonywać działania dodatkowe — logowanie, kompresję, serializację, uwierzytelnianie.
Podczas tworzenia klienta HTTP przez HttpClient { } za pomocą bloku DSL określasz silnik (OkHttp, Android, CIO, Darwin) i instalujesz pluginy. Każdy silnik implementuje niskopoziomowe wysyłanie żądań dla konkretnej platformy: na Androidzie używany jest silnik OkHttp, na iOS — Darwin (URLSession), na Desktop — CIO (Coroutine-based I/O). HttpClient automatycznie wybiera optymalny silnik dla bieżącej platformy.
Żądanie w Ktor Client jest wykonywane przez funkcję suspend, co oznacza pełną integrację z korutynami. Żadnych Callback, żadnych RxJava ani LiveData — tylko sekwencyjny kod z suspend, który działa asynchronicznie bez blokowania wątku.
Potok Ktor składa się z faz: najpierw żądanie przechodzi przez zainstalowane pluginy (np. ContentNegotiation dla JSON, Logging dla logów), następnie silnik wykonuje żądanie HTTP, a odpowiedź ponownie przechodzi przez pluginy w celu deserializacji. Każdy plugin to funkcja suspend, wykonywana w korutynie potoku.
Ważną zaletą potoku Ktor jest możliwość warunkowego przetwarzania. Plugin może sprawdzić URL lub nagłówki żądania i pominąć przetwarzanie, jeśli warunek nie jest spełniony. Na przykład ContentEncoding z gzip jest stosowane tylko do odpowiedzi zawierających nagłówek Content-Encoding: gzip, a Auth działa tylko dla chronionych endpointów, nie wpływając na publiczne API.
Takie podejście potokowe pozwala na elastyczne łączenie pluginów: możesz zainstalować ContentNegotiation z JSON, dodać Auth z tokenem Bearer, włączyć kompresję ContentEncoding i HttpTimeout — i wszystkie będą współpracować w odpowiedniej kolejności. Kolejność instalacji pluginów ma znaczenie: pierwszy zainstalowany będzie przetwarzać żądanie wcześniej niż pozostałe.
Pluginy to modułowy system rozszerzeń Ktor, zastępujący adnotacje Retrofit i przechwytywacze OkHttp. Każdy plugin rozwiązuje konkretne zadanie i jest instalowany przez funkcję install() w bloku HttpClient. Ktor udostępnia wbudowane pluginy, a także pozwala na tworzenie własnych.
| Plugin | Przeznaczenie |
|---|---|
| ContentNegotiation | Serializacja i deserializacja JSON, XML przez Kotlinx Serialization |
| Logging | Logowanie żądań i odpowiedzi z konfiguracją poziomu |
| Auth | Uwierzytelnianie: Basic, Bearer, Digest z automatycznym odświeżaniem tokena |
| HttpTimeout | Konfiguracja limitów czasu połączenia, odczytu i żądania |
| ContentEncoding | Transparentna kompresja gzip i deflate |
| DefaultRequest | Ustawianie wartości domyślnych dla wszystkich żądań |
Do konkretnych zadań tworzy się własny plugin przez createClientPlugin. Plugin może przechwytywać żądanie (onRequest), odpowiedź (onResponse) lub obsługiwać błędy (onError). Całkowicie zastępuje to Interceptor z OkHttp, ale z typowanym API Kotlina i obsługą funkcji suspend.
Własne pluginy są wygodne do dodawania metryk, automatycznej logiki ponawiania, śledzenia żądań lub testowania A/B endpointów. W przeciwieństwie do przechwytywaczy OkHttp, pluginy Ktor są napisane w Kotlinie i działają w kontekście korutyny, co upraszcza obsługę błędów i limitów czasu.
Do debugowania żądań używany jest plugin Logging z poziomem ALL, HEADERS lub BODY. Logging wyświetla metodę, URL, status, nagłówki oraz treść żądania i odpowiedzi. W przeciwieństwie do HttpLoggingInterceptor z OkHttp, Ktor Logging działa asynchronicznie i może być skonfigurowany do filtrowania według poziomu logu (ERROR, WARN, INFO, DEBUG) bez zatrzymywania aplikacji w celu zmiany konfiguracji.
Rozważmy podstawowe żądanie GET przez Ktor Client. Tworzy się HttpClient z zainstalowanym pluginem ContentNegotiation dla JSON. Żądanie jest wykonywane przez funkcję suspend get(), wynik jest automatycznie deserializowany do data class.
data class User(
val login: String,
val id: Int,
val avatarUrl: String
)
val client = HttpClient {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
})
}
}
suspend fun getUser(): User {
return client.get("https://api.github.com/users/octocat").body()
}
Do żądania POST z ciałem używana jest funkcja post() z contentType() i body(). Ktor automatycznie serializuje obiekt do JSON przez zainstalowany ContentNegotiation. Styl DSL sprawia, że kod jest sekwencyjny i czytelny.
data class CreateRepo(
val name: String,
val description: String,
val private: Boolean
)
suspend fun createRepo(): Unit {
val repo = CreateRepo(
name = "my-project",
description = "Sample project",
private = false
)
client.post("https://api.github.com/user/repos") {
contentType(ContentType.Application.Json)
setBody(repo)
}
}
HttpTimeout i DefaultRequest — dwa kluczowe pluginy do konfiguracji. HttpTimeout ustawia limity czasu, a DefaultRequest określa nagłówki i parametry URL dla wszystkich żądań, eliminując powielanie kodu w każdym wywołaniu.
val client = HttpClient {
install(HttpTimeout) {
connectTimeoutMillis = 15000
requestTimeoutMillis = 30000
}
install(DefaultRequest) {
url("https://api.github.com/")
header("Accept", "application/json")
}
}
Wieloplatformowość — główna zaleta Ktor w porównaniu z OkHttp i Retrofit. Ktor Client działa na JVM (Android, Server), Native (iOS, macOS, Windows, Linux) i JS (Browser). Ten sam kod klienta HTTP działa na wszystkich platformach bez zmian, co jest szczególnie cenne dla projektów Kotlin Multiplatform.
Dla każdej platformy Ktor używa własnego silnika (engine). Na Androidzie domyślnie stosowany jest silnik OkHttp, który zapewnia pełną zgodność z ekosystemem OkHttp. Na iOS używany jest DarwinEngine oparty na URLSession. Dla Server — CIOEngine (Coroutine I/O). Silnik można określić jawnie: HttpClient(OkHttp) { } lub HttpClient(Darwin) { }.
Przy wyborze silnika należy uwzględnić jego możliwości: silnik OkHttp obsługuje HTTP/2 i pulę połączeń, DarwinEngine — natywną integrację z siecią iOS i sesje tła URLSession, CIOEngine — czystą implementację korutynową bez zewnętrznych zależności. Dla celów Web używany jest JsEngine lub BrowserEngine działający przez fetch API.
Dzięki jednolitemu API na wszystkich platformach, kod do ładowania danych wygląda tak samo na Androidzie, iOS i Desktop. Zmniejsza to powielanie kodu o 60–80% w projektach KMM w porównaniu z oddzielnymi implementacjami na Retrofit (Android) i URLSession (iOS). Pluginy również działają na wszystkich platformach bez zmian.
Ignorowanie zamykania HttpClient — częsty błąd w Ktor. HttpClient implementuje Closeable i należy go zamknąć po zakończeniu działania aplikacji przez client.close(). W Androidzie robi się to w onDestroy() Activity lub ViewModel.onCleared(). Niezamknięty klient prowadzi do wycieku korutyn i wątków silnika.
Nieprawidłowa kolejność pluginów może zepsuć przetwarzanie żądania. Na przykład ContentNegotiation powinien być zainstalowany przed DefaultRequest, aby typ treści był stosowany poprawnie. Logging zaleca się instalować jako ostatni, aby logować finalną wersję żądania po wszystkich modyfikacjach. Eksperymentuj z kolejnością, jeśli pluginy zachowują się nieoczekiwanie.
Brak obsługi wyjątków w funkcjach suspend. Ktor zgłasza wyjątki IOException przy błędach sieciowych i ClientRequestException przy statusach HTTP 4xx. Blok try-catch jest obowiązkowy dla każdego wywołania get(), post() i innych metod. Użyj HttpResponseValidator w bloku HttpClient do globalnej obsługi błędów bez powielania try-catch w każdej metodzie.
Często zadawane pytania
Ktor używa Kotlin DSL i pluginów bez adnotacji i refleksji. Retrofit jest zbudowany na adnotacjach Javy i refleksji. Ktor obsługuje wieloplatformowość, Retrofit — tylko JVM/Android. Ktor natywnie współpracuje z korutynami, Retrofit dodał suspend przez otoczkę.
Dla Android optymalny jest silnik OkHttp — zapewnia zgodność z ekosystemem OkHttp, pulę połączeń, buforowanie i HTTP/2. Wybieraj go przez HttpClient(OkHttp) { }. Alternatywa — CIOEngine wbudowany w Ktor, ale jest mniej stabilny na Androidzie.
Tak, Ktor obsługuje HTTP/2 przez odpowiedni silnik. Silnik OkHttp dziedziczy obsługę HTTP/2 z OkHttp. DarwinEngine na iOS obsługuje HTTP/2 przez URLSession. CIOEngine obsługuje HTTP/2 po stronie serwera. Wybór silnika określa poziom obsługi protokołu.
Użyj pluginu Auth z ustawieniem bearer { }. Plugin automatycznie dodaje nagłówek Authorization do każdego żądania i może odświeżać token przy odpowiedzi 401 przez refreshTokens. Przykład: install(Auth) { bearer { loadTokens { BearerTokens(token, refreshToken) } } }.
Tak, Ktor Client w pełni działa na iOS przez DarwinEngine, który używa URLSession. Wszystkie pluginy, serializacja i korutyny działają na iOS tak samo jak na Androidzie. To czyni Ktor głównym klientem HTTP dla projektów Kotlin Multiplatform Mobile (KMM).
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również