Step 3 of 7
Ktor client in commonMain, HTTP requests, JSON serialization in shared code
เชื่อม API จาก common code — ใช้ Ktor client ที่ทำงานได้ทุก platform
HTTP calls, JSON parsing, and error handling are the same on every platform. Put them in commonMain. Each platform only provides the HTTP engine.
Versions: Ktor 3.x and kotlinx.serialization 1.8.x.
In shared/build.gradle.kts, add Ktor, serialization, and the engine per platform.
// shared/build.gradle.kts (top plugins)
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization") version "2.4.10"
}
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.ktor:ktor-client-core:3.2.0")
implementation("io.ktor:ktor-client-content-negotiation:3.2.0")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.2.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
}
androidMain.dependencies {
implementation("io.ktor:ktor-client-okhttp:3.2.0")
}
iosMain.dependencies {
implementation("io.ktor:ktor-client-darwin:3.2.0")
}
}
}
Ktor auto-detects the engine (OkHttp on Android, Darwin on iOS) from these dependencies.
Mark classes with @Serializable so they can be converted to and from JSON.
// commonMain
import kotlinx.serialization.Serializable
@Serializable
data class Post(
val id: Int,
val title: String,
val body: String,
)
// commonMain
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
class PostApi {
private val client = HttpClient {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
}
suspend fun getPost(id: Int): Post {
return client.get("https://jsonplaceholder.typicode.com/posts/$id").body()
}
suspend fun createPost(post: Post): Post {
return client.post("https://jsonplaceholder.typicode.com/posts") {
contentType(ContentType.Application.Json)
setBody(post)
}.body()
}
}
// commonMain (suspend fun)
suspend fun demo() {
val api = PostApi()
val post = api.getPost(1)
println(post.title)
}
Diagram: commonMain holds the API and model. Each platform supplies only the HTTP engine.
Loading diagram...
sunt aut facere repellat provident occaecati
Wrap calls in runCatching to handle network errors in common code:
suspend fun safeGet(id: Int): Result<Post> = runCatching { api.getPost(id) }
User data class (id, name, email) with @Serializable.getUser(id: Int) GET request to https://jsonplaceholder.typicode.com/users/1.safeGet wrapper using runCatching.