Retrofit with coroutines

Networking Junior Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Pick the fix

A teammate wraps every Retrofit suspend call in withContext(Dispatchers.IO). What do you tell them?

Options
Hint 1 of 1

Who does the I/O in a Retrofit suspend call?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A It's unnecessary: Retrofit suspend functions are main-safe; switch only for your own blocking work

Correct. OkHttp runs the I/O on its own threads.

Why not the others?
  • B It's required, or the call runs on the main thread. Retrofit already suspends while OkHttp works elsewhere.
  • C Use Dispatchers.Default instead. Still unnecessary for the network call.
  • D Use runBlocking to be safe. That blocks a thread.

30-second answer

Declare an interface with annotated suspend functions, like @GET("users/{id}") suspend fun user(@Path("id") id: String): UserDto, and build it with Retrofit.Builder plus a converter such as kotlinx.serialization. Calling it from a coroutine is main-safe because OkHttp does the I/O on its own threads. Non-2xx responses throw HttpException; network failures throw IOException.

Practise saying it · 35 s. No recording, just a timer

Explain Retrofit with coroutines.

Say your answer out loud, then check which of these points you covered:

  • Interface with suspend functions
  • Converter for JSON
  • Main-safe already
  • HttpException vs IOException
  • One shared instance

Deep dive

Return Response<T> instead when you need headers or want to handle status codes without exceptions.

Share one OkHttpClient and one Retrofit instance through DI.

Map DTOs to app models in the repository.

Cancelling the coroutine cancels the HTTP call.

A Retrofit service Compile-checked · Kotlin 2.4.21
@Serializable data class UserDto(val id: String, val name: String)

interface UserService {
    @GET("users/{id}")
    suspend fun user(@Path("id") id: String): UserDto
}

fun userService(client: OkHttpClient): UserService = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .client(client)
    .addConverterFactory(Json { ignoreUnknownKeys = true }.asConverterFactory("application/json".toMediaType()))
    .build()
    .create(UserService::class.java)

Common mistakes

  • Wrapping Retrofit suspend calls in withContext(Dispatchers.IO) out of habit.
  • Creating a new Retrofit instance per request.
  • Using DTOs directly in UI state.

Senior take & follow-ups

Mention the two exception types and that it's already main-safe.

Follow-up questions

Think of your answer first, then open the hint.

  1. How do you read a header?

    Show hint

    Return Response<T> and read response.headers().

  2. Does cancelling the coroutine cancel the request?

    Show hint

    Yes, Retrofit cancels the underlying OkHttp call.

Similar on iOS: Making a network request URLSession's async API fills Retrofit's role without an interface layer.

Suggest a fix if something here is wrong or out of date.