Handling API Requests Using Ktor (Android)

Handling API Requests Using Ktor (Android)

Asim Latif's photo
May 19, 2024·

5 min read

In this tutorial, we will explore how to use Ktor Android Client to make all types of HTTP requests including GET, POST, PUT, DELETE, and form submissions.

Ktor is a powerful Kotlin-based framework for building asynchronous servers and clients, and it offers a clean and concise API for making HTTP requests from Android applications.

Table of Contents

  • Setup

  • Creating Client

  • Requests Handling

  • Form/File Submissions

  • Summary

  • Useful Links

Setup

In order to process further, make sure you have already setup an Android Project with latest supported libraries and tools.

For Ktor, you need the following dependencies in your app level gradle file:

    implementation "io.ktor:ktor-client-android:$ktor_version"
    implementation "io.ktor:ktor-client-json:$ktor_version"
    implementation "io.ktor:ktor-client-serialization-jvm:$ktor_version"

Make sure to use the latest ktor version, you can find at https://github.com/ktorio/ktor.

In order to support serialization, don't forget to apply KotlinxSerialization plugin. You can find the implementation here.

Creating Http Client Provider:

In order to make requests, Ktor provides us a http client which can be used to make all types of requests. But, in this tutorial we will create a class names HttpClientProvider.kt which will provide us instance of http client.

class ClientProvider {
    fun instance(): HttpClient = HttpClient {
        // Configuration settings for the HttpClient
    }
}

// Implementation

class ClientProvider {
    fun instance(): HttpClient = HttpClient {
        install(ContentNegotiation) {
            json(Json)
        }
        install(ResponseObserver) {
            onResponse {}
        }
        install(HttpTimeout) {
            requestTimeoutMillis = 120000
            connectTimeoutMillis = 120000
        }
        defaultRequest {
            header(HttpHeaders.ContentType, ContentType.Application.Json)
        }
    }
}
Configuration and Plugins
Ktor provides us various plugins to make things easier for us. In the above code snippet, we have added Content Negotiation, Response Observer, Timeout.

You can read more about plugins section at ktor plugins

The instance() function returns an instance of the HttpClient configured with various settings such as content negotiation, response observation, and timeouts. This HttpClient instance will be used throughout our tutorial to make HTTP requests to different endpoints.

Request Handling

Now, let's explore each type of request and how to implement it using Ktor Android Client.

GET

Let's suppose you have an api from which you want to retrieve a list of products.

The following implementation is for the api which returns following data, you need to modify it to meet your needs.

// RAW JSON API DATA
[
  {
    "id": 1,
    "title": "Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops",
    "price": 109.95,
    "description": "Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday",
    "category": "men's clothing",
    "rating": {
      "rate": 3.9,
      "count": 120
    }
  },
  {
    "id": 2,
    "title": "Mens Casual Premium Slim Fit T-Shirts ",
    "price": 22.3,
    "description": "Slim-fitting style, contrast raglan long sleeve, three-button henley placket, light weight & soft fabric for breathable and comfortable wearing. And Solid stitched shirts with round neck made for durability and a great fit for casual fashion wear and diehard baseball fans. The Henley style round neckline includes a three-button placket.",
    "category": "men's clothing",
    "rating": {
      "rate": 4.1,
      "count": 259
    }
  }
]

First we will create our api class named ProductApi.kt. In this class we will create a suspend function to retrieve required data.

class ProductApi {
    val client get()= HttpClientProvider().instance()
    suspend fun getProducts(): List<Product> {
        return client.get(urlString = "$BASE_URL/products/").body<List<Product>>()
    }
}

Here we have created our api but in order to make sure that ktor handles json parsing, make sure that you have added @Serializable annotation on Product.kt class like this:

@Serializable
data class Product(
    val id:Int,
    val title: String,
    val price: Double
}

Now, you can use the created API to request the list of products.

Post

Now we have to add a new product to database using api, we can create another suspend function addProduct(product: Product) which will take an product object and will send to the server.

suspend fun addProduct(): String {
    return client.post(urlString = "$BASE_URL/products/add") {
        contentType(ContentType.Application.Json)
        setBody(Product(/* Product details */))
    }.bodyAsText()
}

In the above function, we are getting the response as text body. But If your api provides a response which is something like:

{
    "message": "Added Successfully",
    "id": "234"
}

You can create a data class names ProductResponse.kt and annotate it with Serializable.

@Serializable
data class ProductResponse(
    val message:String,
    val id:String
)

Now you can modify the above api function as:

suspend fun addProduct(): String {
    return client.post(urlString = "$BASE_URL/products/add") {
        contentType(ContentType.Application.Json)
        setBody(Product(/* Product details */))
    }.body<ProductResponse>()
}

PUT

Put Request is similar to POST, let's say we want to update a product name. We will create a function which will take product id and updated name and will return the response.

suspend fun updateProduct(id: String, name: Boolean): String {
    return client.put(urlString = "$BASE_URL/products/$id") {
        parameter("name", name)
    }.body<ProductResponse>()
}

FORM SUBMISSION

We have another case in which we have to register a user with the details like username, email and password. We can get benefit from Ktor's form submission feature.

Here is the example:

suspend fun signUp(): String {
    return client.submitForm(
        url = "$BASE_URL/signup",
        formParameters = parameters {
            append("username", "userxyz")
            append("email", "example@dummy.com")
            append("password", "password")
            append("confirmation", "password")
        }
    ).bodyAsText()
}

File Uploading

In order to upload an image file, we can use the following approach:

suspend fun uploadFile(): String {
    return client.submitFormWithBinaryData(
        url = "http://example.com/upload",
        formData = formData {
            append("description", "Ktor logo")
            append("image", File("ktor_logo.png").readBytes(), Headers.build {
                append(HttpHeaders.ContentType, "image/png")
                append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
            })
        }
    ).bodyAsText()
}

If you want to read more about file uploading, visit the documentation here

Delete

The delete request implementation is pretty simple and is as follows:

suspend fun deleteProduct(id:Int): String {
    return client.delete("$BASE_URL/products/$id").bodyAsText()
}

Summary

In this tutorial, we've learned how to use Ktor Android Client to make various types of HTTP requests including GET, POST, PUT, DELETE, form submissions, and file uploads. Ktor provides a simple and concise API for interacting with HTTP endpoints, making it a powerful tool for building Android applications that communicate with backend services. Experiment with these examples and explore more features of Ktor to enhance your Android development experience.