Retrofit in Kotlin: A Guide to Using a Type-Safe HTTP Client

Reza Ramesh
4 min readJun 29, 2023

--

Retrofit is a popular library for Android and Java/Kotlin developers that allows them to easily interact with web services using HTTP requests. Retrofit can handle the serialization and deserialization of data, as well as the network communication, using a simple and declarative interface.

In this article, we will learn how to use Retrofit in Kotlin to make GET, POST, PUT and DELETE requests to a RESTful API. We will also see how to use coroutines to perform asynchronous operations with Retrofit.

What is Retrofit?

Retrofit is a type-safe HTTP client that turns your HTTP API into a Kotlin interface. For example, suppose we have an API that provides information about users. We can define an interface like this:

interface UserService {
@GET("users")
suspend fun getUsers(): List<User>
  @GET("users/{id}")
suspend fun getUserById(@Path("id") id: Long): User
@POST("users")
suspend fun createUser(@Body user: User): User
@PUT("users/{id}")
suspend fun updateUser(@Path("id") id: Long, @Body user: User): User
@DELETE("users/{id}")
suspend fun deleteUser(@Path("id") id: Long): Response<Unit>
}

The annotations on the interface methods and parameters indicate how the HTTP request will be handled. For example, @GET specifies the HTTP method and the relative URL, @Path replaces a placeholder in the URL with a value, @Body converts an object into a request body, and suspend marks the function as a coroutine that can be executed asynchronously.

Retrofit can generate an implementation of this interface using the Retrofit.Builder class. For example:

val retrofit = Retrofit.Builder()
.baseUrl("https://example.com/api/")
.addConverterFactory(JacksonConverterFactory.create())
.build()
val userService = retrofit.create(UserService::class.java)

The baseUrl method sets the base URL for all requests. The addConverterFactory method adds a converter factory that can serialize and deserialize data using a library like Jackson, Gson or Moshi. The create method creates an instance of the interface using reflection.

How to make HTTP requests with Retrofit?

Once we have an instance of the interface, we can use it to make HTTP requests to the API. For example, to get a list of users, we can write:

val users = userService.getUsers()

This will make a GET request to https://example.com/api/users and return a list of User objects. Similarly, to create a new user, we can write:

val user = User(name = "Alice", age = 25)
val createdUser = userService.createUser(user)

This will make a POST request to https://example.com/api/users with the user object as the request body and return the created user object.

To update an existing user, we can write:

val user = User(name = "Bob", age = 30)
val updatedUser = userService.updateUser(1L, user)

This will make a PUT request to https://example.com/api/users/1 with the user object as the request body and return the updated user object.

To delete an existing user, we can write:

val response = userService.deleteUser(1L)

This will make a DELETE request to https://example.com/api/users/1 and return a response object that contains information about the status code, headers and body of the response.

How to use coroutines with Retrofit?

One of the advantages of using Retrofit in Kotlin is that we can use coroutines to perform asynchronous operations without blocking the main thread. Coroutines are lightweight threads that can be suspended and resumed without blocking other coroutines.

To use coroutines with Retrofit, we need to mark our interface functions with the suspend modifier. This tells the compiler that these functions can be called from within a coroutine scope and that they can be suspended until the result is available.

We also need to add the kotlinx-coroutines-android dependency to our project:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'

Then, we can use the lifecycleScope or viewModelScope extensions from the androidx.lifecycle:lifecycle-runtime-ktx library to launch coroutines in our activities or view models:

implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'

For example, to get a list of users and display them in a RecyclerView, we can write:

class MainActivity : AppCompatActivity() {
  private val userService: UserService by lazy {
val retrofit = Retrofit.Builder()
.baseUrl("https://example.com/api/")
.addConverterFactory(JacksonConverterFactory.create())
.build()
retrofit.create(UserService::class.java)
}
private val adapter: UserAdapter by lazy {
UserAdapter()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.adapter = adapter lifecycleScope.launch {
try {
val users = userService.getUsers()
adapter.submitList(users)
} catch (e: Exception) {
Toast.makeText(this@MainActivity, e.message, Toast.LENGTH_SHORT).show()
}
}
}
}

The lifecycleScope.launch function creates a coroutine that is bound to the lifecycle of the activity. The try-catch block handles any exceptions that may occur during the network request. The submitList function updates the RecyclerView with the list of users.

Conclusion

In this article, we learned how to use Retrofit in Kotlin to make HTTP requests to a RESTful API. We also learned how to use coroutines to perform asynchronous operations with Retrofit.

Retrofit is a powerful and easy-to-use library that can simplify the integration of web services in our Android and Java/Kotlin applications.

--

--

Reza Ramesh

I am an Android developer and UI/UX designer with 4 years of experience in creating engaging and user-friendly mobile applications