Free tutorials & notes in Hindi & English · Clean code examples 🧰 Free PDF & Image Tools →
💾 Data, Network & Architecture  ·  Lesson 33

Retrofit: Call REST APIs

What is a REST API?

A REST API is a web address that returns data (usually JSON) instead of a web page. Apps call it to get products, post orders or log in. Retrofit turns a Kotlin interface into those HTTP calls and Gson converts JSON into Kotlin objects.

App (Retrofit)Server (PHP / Next.js)DatabaseGET /api/android-tips.json(optional) SELECT …200 OK + JSONGson → List<Tip> → show on screen
One API call

The JSON we will read

We created a real JSON endpoint on codingeasily.in for the Student Diary "Tip of the day":

JSON
GET https://codingeasily.in/api/android-tips.json { "tips": [ { "id": 1, "title": "Use View Binding", "text": "Replace findViewById with binding.myView – it is null-safe and type-safe.", "image": "https://codingeasily.in/assets/img/tutorials/android/tip-1.png", "link": "https://codingeasily.in/tutorial/android/android-view-binding/" }, … ] }

Step 1: Permission and dependencies

XML + TOML + Kotlin DSL
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />

# libs.versions.toml
retrofit = "2.11.0"
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
retrofit-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }

// app/build.gradle.kts
implementation(libs.retrofit)
implementation(libs.retrofit.converter.gson)

Step 2: Model classes

Property names must match the JSON keys (or use @SerializedName("json_key")).

Kotlin – network/Tip.kt
package com.codingeasily.studentdiary.network

/** Matches one object in https://codingeasily.in/api/android-tips.json */
data class Tip(
    val id: Int,
    val title: String,
    val text: String,
    val image: String,
    val link: String
)

data class TipsResponse(val tips: List<Tip>)

Step 3: API interface + Retrofit

Kotlin – network/TipsApi.kt
package com.codingeasily.studentdiary.network

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET

/** Retrofit turns this interface into real HTTP code. */
interface TipsApi {
    @GET("api/android-tips.json")
    suspend fun getTips(): TipsResponse

    companion object {
        private const val BASE_URL = "https://codingeasily.in/"

        val service: TipsApi by lazy {
            Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(TipsApi::class.java)
        }
    }
}

Step 4: Call it from a ViewModel

Kotlin
// From Student Diary – NoteViewModel
private val _tip = MutableLiveData<TipState>()
val tip: LiveData<TipState> = _tip

fun loadTip(force: Boolean = false) {
    if (!force && _tip.value is TipState.Success) return
    _tip.value = TipState.Loading
    viewModelScope.launch {
        _tip.value = try {
            val tips = TipsApi.service.getTips().tips
            if (tips.isEmpty()) TipState.Error("No tips yet")
            else TipState.Success(tips[(System.currentTimeMillis() / 86_400_000L % tips.size).toInt()])
        } catch (e: Exception) {
            TipState.Error(e.message ?: "Network error")
        }
    }
}
9:41▮▮ 85%Progress⋮🖼️tip-1.png loaded by GlideTip of the dayUse View BindingReplace findViewById with binding…Read more📝Notes📚Subjects📊Progress
The tip card in Student Diary

Query, path, POST and headers

These are exactly the kinds of calls the KartEasy e-commerce app makes to its PHP API:

Kotlin
interface ShopApi {
    // GET products.php?category=3&page=1
    @GET("products.php")
    suspend fun products(@Query("category") categoryId: Int, @Query("page") page: Int = 1): ProductListResponse

    // GET product/15
    @GET("product/{id}")
    suspend fun product(@Path("id") id: Int): ProductResponse

    // POST JSON body {"email": "...", "password": "..."}
    @POST("auth/login.php")
    suspend fun login(@Body body: LoginRequest): LoginResponse

    // POST form fields (application/x-www-form-urlencoded)
    @FormUrlEncoded
    @POST("feedback.php")
    suspend fun feedback(@Field("name") name: String, @Field("message") message: String): SimpleResponse

    // header for logged-in calls
    @GET("orders.php")
    suspend fun orders(@Header("Authorization") bearer: String): OrdersResponse

    // when you need the status code
    @GET("profile.php")
    suspend fun profile(): Response<ProfileResponse>
}
AnnotationResult
@Query("q")?q=value
@Path("id")replaces {id} in the path
@Bodyobject sent as JSON
@Field + @FormUrlEncodedform fields (like an HTML form)
@Headerone header for this call

Errors, timeouts and logging

Kotlin
try {
    val res = api.profile()                      // Response<ProfileResponse>
    if (res.isSuccessful) show(res.body()!!)
    else if (res.code() == 401) logout()
    else showError("Server error ${res.code()}")
} catch (e: IOException) {                       // no internet, timeout
    showError("Check your internet connection")
} catch (e: HttpException) {                     // non-2xx when return type is not Response<>
    showError("HTTP ${e.code()}")
}

// timeouts + logging (add com.squareup.okhttp3:logging-interceptor)
val client = OkHttpClient.Builder()
    .connectTimeout(15, TimeUnit.SECONDS)
    .readTimeout(20, TimeUnit.SECONDS)
    .addInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY })
    .build()
Retrofit.Builder().baseUrl(BASE_URL).client(client).addConverterFactory(GsonConverterFactory.create()).build()

Common mistakes

  • No INTERNET permission → SecurityException / "Permission denied".
  • Calling the API from the main thread without suspend → NetworkOnMainThreadException.
  • Field names not matching the JSON → values are null (or 0).
  • Creating a new Retrofit instance for every call → slow; create it once (lazy singleton).
  • Release build crashes with R8 → keep your model classes (see proguard-rules.pro in the project).

Summary

  • INTERNET permission + Retrofit + Gson converter.
  • Data classes match JSON; interface methods are suspend with @GET/@POST.
  • Call from viewModelScope, wrap in try/catch, expose a sealed UI state.
✅ Practice

Call https://codingeasily.in/api/android-tips.json and show all tips in a RecyclerView (title + text). Add pull-to-refresh with SwipeRefreshLayout.

Frequently Asked Questions

Why must the base URL end with a slash?
Retrofit joins the base URL and the path from @GET. Without the trailing / it throws "baseUrl must end in /".
Can I call http:// (not https) URLs?
Android blocks clear-text HTTP by default since Android 9. Use HTTPS. For a local test server you can allow it with a network security config, but never ship that.
Gson, Moshi or kotlinx.serialization?
All work with Retrofit. Gson is the simplest to start with and used in this course; Moshi and kotlinx.serialization are more Kotlin-friendly for larger apps.
← Android Tutorial (Kotlin)
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

Next topicLoad Images with Glide Your progress: 33 / 43 (77%) · Lesson 34 / 43 Continue →

How was this lesson?

Your feedback helps us make the content better.

Write a review or suggestion (optional)

After approval, your name and city may appear publicly with your review. Please do not write your email or phone number.

▶ CodingEasily on YouTube

Learn with video lessons in easy Hindi

Featured video: RDBMS Part 3 – Class XI Commerce IT

▶ Subscribe – it’s free