| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Small Kotlin Multiplatform helpers that make Ktor client calls easier to return, inspect, and handle.
sourceSets {
val commonMain by getting {
dependencies {
implementation("io.github.androidpoet:ktor-boost:$version")
}
}
}For typed WebSocket and SSE helpers, add the optional realtime module:
implementation("io.github.androidpoet:ktor-realtime:$version")Or pick protocol-specific modules:
implementation("io.github.androidpoet:ktor-realtime-websocket:$version")
implementation("io.github.androidpoet:ktor-realtime-sse:$version")
implementation("io.github.androidpoet:ktor-realtime-reverb:$version")
implementation("io.github.androidpoet:ktor-realtime-socketio:$version")
implementation("io.github.androidpoet:ktor-realtime-stomp:$version")
implementation("io.github.androidpoet:ktor-realtime-graphql:$version")
implementation("io.github.androidpoet:ktor-realtime-mqtt:$version")
implementation("io.github.androidpoet:ktor-realtime-rsocket:$version")
implementation("io.github.androidpoet:ktor-realtime-longpolling:$version")Use getResult, postResult, putResult, deleteResult, patchResult, headResult, or optionsResult when you want a simple Kotlin Result<T>.
val result = httpClient.getResult<List<Movie>>("trendingMovies")
result
.onSuccess { movies ->
// render movies
}
.onFailure { error ->
// show error
}If you want non-2xx HTTP responses to become Result.failure(...), configure Ktor with expectSuccess = true:
val httpClient = HttpClient {
expectSuccess = true
}| Use case | API |
|---|---|
| Simple success/failure handling | getResult<T>() |
| Need status code, headers, or error body | getNetworkResult<T, E>() |
| Authenticated request with token refresh | getAuthenticatedNetworkResult<T, E>() |
| Retry transient failures | getResultWithRetry<T>() |
| Download bytes with progress | downloadBytes() |
| Empty response body, such as 204 No Content | deleteResult<Unit>() |
| Need a Deferred<Result<T>> | getResultAsync<T>() |
| Add common headers, query params, or body | bearerToken, queryParams, jsonBody, formBody |
| Need suspend callbacks on Result | onSuccessSuspend, onFailureSuspend, foldSuspend |
Use NetworkResult when your app needs response metadata or typed API errors. Detailed operator docs: docs/network-result-operators.md.
val result = httpClient.getNetworkResult<User, ApiError>(
urlString = "users/me",
decodeErrorBody = { rawBody ->
json.decodeFromString<ApiError>(rawBody)
},
)
when (result) {
is NetworkResult.Success -> {
val user = result.body
val statusCode = result.statusCode
val headers = result.headers
}
is NetworkResult.HttpError -> {
val statusCode = result.statusCode
val rawErrorBody = result.rawBody
val apiError = result.errorBody
}
is NetworkResult.ResponseDecodingError -> {
val cause = result.cause
}
is NetworkResult.RequestError -> {
val cause = result.cause
}
}NetworkResult works with or without Ktor's expectSuccess setting.
Use Boost operators when you want a more expressive app-facing API:
httpClient.getResult<User, ApiError>("users/me", json)
.onSuccess { result ->
render(result.body)
}
.onUnauthorized {
session.refreshToken()
}
.onRateLimited { rateLimit, _ ->
scheduleRetry(rateLimit.retryAfterSeconds)
}
.recoverRequestError {
cache.user()
}
.onError { result ->
showMessage(result.messageOrNull ?: "Something went wrong")
}For realtime chat or presence streams, add ktor-realtime. Detailed chat module docs: docs/chat-module.md.
httpClient.realtimeChat<ChatEvent, ChatCommand>(
urlString = "wss://example.com/chat",
onMessage = { event ->
render(event)
},
) {
sendJson(ChatCommand.Join(roomId = "general"))
}Protocol-neutral entrypoint:
val endpoint = RealtimeEndpoint.WebSocket("wss://example.com/realtime")
httpClient.realtime<ChatEvent, ChatCommand>(
endpoint = endpoint,
onEvent = { event -> render(event) },
)WebSocket, ServerSentEvents, Reverb, Socket.IO, STOMP, GraphQL subscriptions, MQTT-over-WS, RSocket, and long-polling are implemented with protocol-specific entrypoints. Protocol-specific entrypoints are split as dedicated APIs (realtimeReverb, realtimeSocketIo, realtimeStomp, realtimeGraphQlSubscriptions, realtimeMqttOverWebSocket, realtimeRSocket, and realtimeLongPolling).
End-to-end realtime tests are available for WebSocket, SSE, and long-polling.
./gradlew realtimeIntegrationTestThis task:
If Docker is not installed/running, use local unit tests instead:
./gradlew :ktor-realtime:desktopTestYou can also use convenience helpers:
val user = result.getOrNull()
val apiError = result.errorOrNull()
val statusCode = result.statusCodeOrNull()
val displayName =
result
.map { user -> user.name }
.getOrNull()Use BearerTokenProvider when authenticated APIs need automatic token refresh. Your app owns token storage; KtorBoost asks for the current token, refreshes when needed, and replays the request after a 401.
class AppTokenProvider(
private val tokenStore: TokenStore,
private val authApi: AuthApi,
) : BearerTokenProvider {
override suspend fun currentToken(): String? {
return tokenStore.accessToken
}
override suspend fun refreshToken(): String? {
val token = authApi.refreshAccessToken(tokenStore.refreshToken)
tokenStore.accessToken = token
return token
}
override suspend fun clearToken() {
tokenStore.clear()
}
}Then call authenticated helpers:
val result = httpClient.getAuthenticatedNetworkResult<User, ApiError>(
urlString = "users/me",
tokenProvider = appTokenProvider,
decodeErrorBody = { rawBody ->
json.decodeFromString<ApiError>(rawBody)
},
)For simple Result<T>:
val result = httpClient.getAuthenticatedResult<User>(
urlString = "users/me",
tokenProvider = appTokenProvider,
)Behavior:
Use retry helpers for transient failures such as 408, 429, 500, 502, 503, and 504.
val result = httpClient.getResultWithRetry<User>(
urlString = "users/me",
retryPolicy = RetryPolicy(maxRetries = 3),
timeout = 5.seconds,
)For typed errors:
val result = httpClient.getNetworkResultWithRetry<User, ApiError>(
urlString = "users/me",
retryPolicy = RetryPolicy(maxRetries = 3),
timeout = 5.seconds,
decodeErrorBody = { rawBody ->
json.decodeFromString<ApiError>(rawBody)
},
)Use downloadBytes for KMP-safe downloads with progress. The core API returns bytes and metadata; apps can decide where to store the bytes on each platform.
val result = httpClient.downloadBytes(
urlString = "files/report.pdf",
onProgress = { progress ->
val fraction = progress.fraction
val bytesRead = progress.bytesRead
val totalBytes = progress.totalBytes
},
)
when (result) {
is DownloadResult.Success -> {
val bytes = result.content.bytes
val contentType = result.content.contentType
val contentLength = result.content.contentLength
}
is DownloadResult.HttpError -> {
val statusCode = result.statusCode
val rawErrorBody = result.rawBody
}
is DownloadResult.RequestError -> {
val cause = result.cause
}
}DownloadResult.Success includes:
DownloadProgress includes:
Use small request builder helpers to keep call sites readable.
val result = httpClient.postResult<User>("users") {
bearerToken(token)
queryParams(mapOf("source" to "android"))
jsonBody(CreateUserRequest(name = "Ranbir"))
}For endpoints that return no body, request Unit.
val result = httpClient.deleteResult<Unit>("users/123")Async helpers return Deferred<Result<T>>.
val deferredResult = httpClient.getResultAsync<List<Movie>>("trendingMovies")
val result = deferredResult.await()KtorBoost also includes suspend-friendly Result helpers:
result
.onSuccessSuspend { movies ->
repository.save(movies)
}
.onFailureSuspend { error ->
logger.log(error)
}val message =
result.foldSuspend(
onSuccess = { movies -> "Loaded ${movies.size} movies" },
onFailure = { error -> error.message ?: "Something went wrong" },
)Existing simple helpers are still available:
Recommended release version: 1.1.0.
This release adds NetworkResult, auth refresh helpers, retry helpers, request builder shortcuts, downloads, and fixes coroutine behavior:
Contributions are welcome! If you've found a bug, have an idea for an improvement, or want to contribute new features, please open an issue or submit a pull request.
Support it by joining stargazers for this repository. ⭐
Also, follow me on GitHub for my next creations! 🤩
Copyright 2023 AndroidPoet (Ranbir Singh)
Licensed under the Apache License, Version 2.0.
See LICENSE.txt for details.
| Back | FazBrowse Home | New Git URL |