Skip to main content

Kotlin Examples

Complete Kotlin usage examples for MochaJSON v1.0.0, showcasing the new stateless library design with coroutines integration.

v1.0.0 Stateless Design Examples

Basic GET with Production-Safe Defaults

import io.mochaapi.client.*

fun main() {
try {
// Simple static API - uses production defaults
val data = Api.get("https://jsonplaceholder.typicode.com/users/1")
.execute()
.toMap()

println("Name: ${data["name"]}")
println("Email: ${data["email"]}")

} catch (e: ApiException) {
println("API Error: ${e.message}")
} catch (e: JsonException) {
println("JSON Error: ${e.message}")
}
}

Development with Localhost Access

import io.mochaapi.client.*
import java.time.Duration

fun main() {
try {
// ApiClient for localhost development
val devClient = ApiClient.Builder()
.allowLocalhost(true) // Allow localhost for development
.connectTimeout(Duration.ofSeconds(10))
.readTimeout(Duration.ofSeconds(30))
.enableRetry() // Simple retry with 3 attempts
.enableLogging() // Console logging
.build()

// Use the client
val user = devClient.get("http://localhost:8080/api/users/1")
.execute()
.toMap()

println("Local user: ${user["name"]}")

} catch (e: ApiException) {
println("API Error: ${e.message}")
} catch (e: JsonException) {
println("JSON Error: ${e.message}")
}
}

Kotlin Idiomatic Usage

import io.mochaapi.client.*
import java.time.Duration

// Extension functions for cleaner syntax
fun <T> ApiRequest.fetchAs(clazz: Class<T>): T =
execute().to(clazz)

fun main() {
try {
val user = Api.get("https://jsonplaceholder.typicode.com/users/1")
.fetchAs(User::class.java)

println("User: ${user.name}")

} catch (e: Exception) {
println("Error: ${e.message}")
}
}

data class User(
val id: Int,
val name: String,
val email: String
)

Kotlin DSL Pattern

import io.mochaapi.client.*
import java.time.Duration

fun main() {
try {
// Builder with Kotlin DSL
val client = ApiClient.Builder().apply {
connectTimeout(Duration.ofSeconds(10))
readTimeout(Duration.ofSeconds(30))
enableRetry()
allowLocalhost(true)
enableLogging()
}.build()

val user = client.get("https://jsonplaceholder.typicode.com/users/1")
.execute()
.toMap()

println("User: ${user["name"]}")

} catch (e: Exception) {
println("Error: ${e.message}")
}
}

Coroutines Integration

import io.mochaapi.client.*
import kotlinx.coroutines.*
import java.time.Duration

fun main() = runBlocking {
try {
val client = ApiClient.Builder()
.connectTimeout(Duration.ofSeconds(10))
.build()

// Async with coroutines
val user = fetchUser(client, 1)
println("User: ${user.name}")

} catch (e: Exception) {
println("Error: ${e.message}")
}
}

suspend fun fetchUser(client: ApiClient, id: Int): User = withContext(Dispatchers.IO) {
client.get("https://jsonplaceholder.typicode.com/users/$id")
.execute()
.to(User::class.java)
}

data class User(
val id: Int,
val name: String,
val email: String
)

Multiple Clients in Kotlin

import io.mochaapi.client.*
import java.time.Duration

object ApiClients {
val github by lazy {
ApiClient.Builder()
.connectTimeout(Duration.ofSeconds(10))
.requestInterceptor { req ->
req.header("Authorization", "Bearer ${getGithubToken()}")
req
}
.build()
}

val internal by lazy {
ApiClient.Builder()
.allowLocalhost(true)
.connectTimeout(Duration.ofSeconds(5))
.build()
}

private fun getGithubToken(): String = System.getenv("GITHUB_TOKEN") ?: ""
}

fun main() {
try {
// Use different clients
val githubUser = ApiClients.github.get("https://api.github.com/user")
.execute()
.toMap()

val internalUser = ApiClients.internal.get("http://localhost:8080/api/user")
.execute()
.toMap()

println("GitHub user: ${githubUser["login"]}")
println("Internal user: ${internalUser["name"]}")

} catch (e: Exception) {
println("Error: ${e.message}")
}
}

Advanced Coroutines Example

import io.mochaapi.client.*
import kotlinx.coroutines.*
import java.time.Duration

class UserService {
private val client = ApiClient.Builder()
.connectTimeout(Duration.ofSeconds(10))
.enableRetry()
.build()

suspend fun getUser(id: Int): User = withContext(Dispatchers.IO) {
client.get("https://jsonplaceholder.typicode.com/users/$id")
.execute()
.to(User::class.java)
}

suspend fun getUsers(ids: List<Int>): List<User> = coroutineScope {
ids.map { id ->
async { getUser(id) }
}.awaitAll()
}

suspend fun createUser(user: User): User = withContext(Dispatchers.IO) {
client.post("https://jsonplaceholder.typicode.com/users")
.body(user)
.execute()
.to(User::class.java)
}
}

fun main() = runBlocking {
try {
val userService = UserService()

// Fetch multiple users concurrently
val users = userService.getUsers(listOf(1, 2, 3))
users.forEach { user ->
println("User: ${user.name}")
}

// Create a new user
val newUser = User(name = "John Doe", email = "john@example.com")
val createdUser = userService.createUser(newUser)
println("Created user: ${createdUser.name}")

} catch (e: Exception) {
println("Error: ${e.message}")
}
}

data class User(
val id: Int = 0,
val name: String,
val email: String
)

Stateless Resource Management

import io.mochaapi.client.*
import java.time.Duration

fun main() {
try {
// No shutdown needed - library is stateless
// Create clients as needed
val client1 = ApiClient.Builder()
.connectTimeout(Duration.ofSeconds(10))
.build()

val client2 = ApiClient.Builder()
.allowLocalhost(true)
.connectTimeout(Duration.ofSeconds(5))
.build()

// Use them independently, no cleanup required
val data1 = client1.get("https://api1.example.com/data")
.execute()
.toMap()

val data2 = client2.get("http://localhost:8080/api/data")
.execute()
.toMap()

println("External data: ${data1["value"]}")
println("Local data: ${data2["value"]}")

} catch (e: Exception) {
println("Error: ${e.message}")
}
}

Traditional Examples (Still Work in v1.0.0)

GET Request with Map Parsing

import io.mochaapi.client.*

fun main() {
// GET request → Map
val post = Api.get("https://jsonplaceholder.typicode.com/posts/1")
.execute()
.toMap()

println("Post ID: ${post["id"]}")
println("Post Title: ${post["title"]}")
println("Post Body: ${post["body"]}")
}

Sample JSON Response:

FieldTypeSample Value
userIdInt1
idInt1
titleString"sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
bodyString"quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"

GET Request with Data Class

import io.mochaapi.client.*

data class Post(
val id: Int,
val userId: Int,
val title: String,
val body: String
)

fun main() {
// GET request → Data class
val post = Api.get("https://jsonplaceholder.typicode.com/posts/1")
.execute()
.to(Post::class.java)

println("Post ID: ${post.id}")
println("Post Title: ${post.title}")
println("Post Body: ${post.body}")
}

POST Request with JSON Body

import io.mochaapi.client.*

data class Post(
val id: Int,
val userId: Int,
val title: String,
val body: String
)

fun main() {
// Create request body
val newPost = mapOf(
"title" to "My Kotlin Post",
"body" to "This post was created from Kotlin!",
"userId" to 1
)

// POST request
val response = Api.post("https://jsonplaceholder.typicode.com/posts")
.body(newPost)
.execute()

println("Status Code: ${response.code()}")

// Parse response to Map
val responseMap = response.toMap()
println("Created Post ID: ${responseMap["id"]}")
println("Created Post Title: ${responseMap["title"]}")

// Parse response to Data class
val createdPost = response.to(Post::class.java)
println("Created Post: ${createdPost.title}")
}

Sample Response JSON:

FieldTypeSample Value
idInt101
titleString"My Kotlin Post"
bodyString"This post was created from Kotlin!"
userIdInt1

Query Parameters and Headers

import io.mochaapi.client.*

fun main() {
val response = Api.get("https://jsonplaceholder.typicode.com/posts")
.query("userId", 1)
.query("_limit", 5)
.header("Authorization", "Bearer token123")
.header("User-Agent", "MyApp/1.0")
.execute()

val posts = response.toList()
println("Found ${posts.size} posts")

// Process each post
posts.forEachIndexed { index, postObj ->
val post = postObj as Map<String, Any>
println("Post ${post["id"]}: ${post["title"]}")
}
}

Async Requests

import io.mochaapi.client.*

fun main() {
Api.get("https://jsonplaceholder.typicode.com/posts/1")
.async { response ->
println("Async response: ${response.body()}")
println("Status: ${response.code()}")
}

// Main thread continues...
println("Request sent asynchronously")
}

Complex Nested Objects

import io.mochaapi.client.*

data class User(
val id: Int,
val name: String,
val username: String,
val email: String,
val phone: String,
val website: String,
val address: Address,
val company: Company
)

data class Address(
val street: String,
val suite: String,
val city: String,
val zipcode: String,
val geo: Geo
)

data class Geo(
val lat: String,
val lng: String
)

data class Company(
val name: String,
val catchPhrase: String,
val bs: String
)

fun main() {
val user = Api.get("https://jsonplaceholder.typicode.com/users/1")
.execute()
.to(User::class.java)

println("User Name: ${user.name}")
println("User Email: ${user.email}")
println("User Address: ${user.address.street}, ${user.address.city}")
println("User Company: ${user.company.name}")
}

Sample User JSON Response:

FieldTypeSample Value
idInt1
nameString"Leanne Graham"
emailString"Sincere@april.biz"
phoneString"1-770-736-8031 x56442"
websiteString"hildegard.org"
address.streetString"Kulas Light"
address.cityString"Gwenborough"
company.nameString"Romaguera-Crona"

Error Handling

import io.mochaapi.client.*
import io.mochaapi.client.exception.*

data class Post(
val id: Int,
val userId: Int,
val title: String,
val body: String
)

fun main() {
try {
val response = Api.get("https://jsonplaceholder.typicode.com/posts/1")
.execute()

if (response.isError()) {
println("HTTP Error: ${response.code()}")
return
}

val post = response.to(Post::class.java)
println("Success: ${post.title}")

} catch (e: ApiException) {
println("Network/HTTP Error: ${e.message}")
} catch (e: JsonException) {
println("JSON Parsing Error: ${e.message}")
} catch (e: Exception) {
println("Unexpected Error: ${e.message}")
}
}

List Processing

import io.mochaapi.client.*

data class Post(
val id: Int,
val userId: Int,
val title: String,
val body: String
)

fun main() {
val response = Api.get("https://jsonplaceholder.typicode.com/posts")
.query("userId", 1)
.query("_limit", 3)
.execute()

val posts = response.toList()
println("Found ${posts.size} posts for user 1")

posts.forEach { postObj ->
val post = postObj as Map<String, Any>
println("Post ${post["id"]}: ${post["title"]}")
}
}

Nested JSON Access with JsonMap

JsonMap provides a clean, chainable API for accessing nested JSON data without casting boilerplate.

import io.mochaapi.client.*

fun main() {
try {
// Example: User profile API with nested data
val json = Api.get("https://api.example.com/user/123")
.execute()
.toJsonMap()

// Traditional approach (verbose with casting)
val data = json.toMap()
val user = data["data"] as Map<String, Any>
val name = user["name"] as Map<String, Any>
val location = user["location"] as Map<String, Any>
val street = location["street"] as Map<String, Any>
val coordinates = location["coordinates"] as Map<String, Any>

val traditionalName = "${name["first"]} ${name["last"]}"
val traditionalAddress = "${street["number"]} ${street["name"]}, ${location["city"]}"
val traditionalLat = coordinates["latitude"].toString()

// JsonMap approach (clean chaining)
val cleanName = "${json.get("data").get("name").get("first")} ${json.get("data").get("name").get("last")}"
val cleanAddress = "${json.get("data").get("location").get("street").get("number")} " +
"${json.get("data").get("location").get("street").get("name")}, " +
"${json.get("data").get("location").get("city")}"
val cleanLat = json.get("data").get("location").get("coordinates").get("latitude").toString()

println("Name: $cleanName")
println("Address: $cleanAddress")
println("Latitude: $cleanLat")

// Intermediate access for complex operations
val userData = json.get("data")
val locationData = userData.get("location")

val email = userData.get("email").toString()
val city = locationData.get("city").toString()
val state = locationData.get("state").toString()

println("Email: $email")
println("Location: $city, $state")

// Kotlin extension functions for even cleaner syntax
fun JsonMap.safeGet(key: String): String = get(key).toString()

val firstName = json.get("data").get("name").safeGet("first")
val lastName = json.get("data").get("name").safeGet("last")
val fullName = "$firstName $lastName"

println("Full name: $fullName")

} catch (e: Exception) {
println("Error: ${e.message}")
}
}

Benefits for Kotlin developers:

  • No casting: Eliminates as Map<String, Any> casts
  • Null safety: Works well with Kotlin's null safety features
  • Extension functions: Can create custom extension functions for cleaner syntax
  • Coroutines friendly: Works seamlessly with async operations

Functional Style with Extension Functions

import io.mochaapi.client.*

// Extension function for cleaner syntax
fun ApiRequest.executeToMap(): Map<String, Any> = execute().toMap()

fun main() {
val posts = Api.get("https://jsonplaceholder.typicode.com/posts")
.query("userId", 1)
.query("_limit", 2)
.executeToMap()

println("Posts: $posts")
}

Next Steps