Kotlin and APIs: The Three Pillars of Modern Development
\n\nHave you ever wondered why some applications fly while others lag even on flagship devices? The secret often lies not in the hardware, but in how they communicate with the server. If you're writing in Kotlin — whether it's a backend with Ktor or an Android app — sooner or later you'll face a choice: REST, GraphQL, or gRPC?
\n\nAt PythonLib, we love to break down technologies to the smallest detail. Today, we'll break down three approaches to working with APIs in Kotlin. No fluff: just code, comparisons, and real-world cases. Let's go!
\n\nREST API in Kotlin: A Classic That Never Gets Old
\n\nREST is like a black suit: appropriate everywhere, simple, and understandable. In Kotlin, there are two main players for REST: Ktor (for the server side) and Retrofit (for the client side).
\n\nServer with Ktor: Write an API in 5 Minutes
\n\nKtor is a framework from JetBrains, created specifically for Kotlin. It's asynchronous, lightweight, and doesn't impose unnecessary dependencies.
\n\n// build.gradle.kts\nimplementation("io.ktor:ktor-server-netty:2.3.7")\nimplementation("io.ktor:ktor-server-content-negotiation:2.3.7")\nimplementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")\n\nAnd now the server itself:
\n\nimport io.ktor.server.engine.*\nimport io.ktor.server.netty.*\nimport io.ktor.server.routing.*\nimport io.ktor.server.response.*\nimport io.ktor.serialization.kotlinx.json.*\nimport io.ktor.server.plugins.contentnegotiation.*\nimport kotlinx.serialization.Serializable\n\n@Serializable\ndata class User(val id: Int, val name: String, val email: String)\n\nfun main() {\n embeddedServer(Netty, port = 8080) {\n install(ContentNegotiation) {\n json()\n }\n routing {\n get("/api/users") {\n val users = listOf(\n User(1, "Anna", "anna@example.com"),\n User(2, "Boris", "boris@example.com")\n )\n call.respond(users)\n }\n get("/api/users/{id}") {\n val id = call.parameters["id"]?.toIntOrNull()\n if (id != null) {\n call.respond(User(id, "Test", "test@example.com"))\n } else {\n call.respondText("Invalid ID", status = io.ktor.http.HttpStatusCode.BadRequest)\n }\n }\n }\n }.start(wait = true)\n}\n\nThis code is a fully functional REST API in Kotlin with two endpoints. Ktor serializes data into JSON automatically thanks to kotlinx.serialization.
Client with Retrofit: Connect to Any API
\n\nRetrofit is the de facto standard for Android development. It works perfectly with Kotlin thanks to coroutines.
\n\n// build.gradle.kts\nimplementation("com.squareup.retrofit2:retrofit:2.9.0")\nimplementation("com.squareup.retrofit2:converter-gson:2.9.0")\nimplementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")\n\nDeclare the interface:
\n\ninterface ApiService {\n @GET("users")\n suspend fun getUsers(): List<User>\n\n @GET("users/{id}")\n suspend fun getUser(@Path("id") userId: Int): User\n\n @POST("users")\n suspend fun createUser(@Body user: User): User\n}\n\nCreate the client and make a request:
\n\nval retrofit = Retrofit.Builder()\n .baseUrl("https://api.example.com/")\n .addConverterFactory(GsonConverterFactory.create())\n .build()\n\nval service = retrofit.create(ApiService::class.java)\n\n// In a coroutine\nGlobalScope.launch {\n val users = service.getUsers()\n println(users)\n}\n\nREST Pros: simplicity, HTTP caching, huge community.
\nCons: over-fetching and under-fetching of data — you often get more or less than you need.