Skip to main content

Complete examples

Full API definition

// ApiSpec.kt
package dev.kolibrium.api.example

import dev.kolibrium.api.core.ApiSpec
import dev.kolibrium.api.ksp.annotations.GenerateApi

@GenerateApi
object VinylStoreApiSpec : ApiSpec(baseUrl = "https://api.example.com")
// models/Vinyl.kt
package dev.kolibrium.api.example.models

import dev.kolibrium.api.ksp.annotations.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient

// Request models
@POST("/api/vinyls")
@Returns(Vinyl::class)
@Serializable
data class CreateVinylRequest(
var artist: String? = null,
var album: String? = null,
var year: Int? = null,
var genre: String? = null,
var price: Double? = null,
var stock: Int? = null
)

@GET("/api/vinyls/{id}")
@Returns(Vinyl::class)
@Serializable
data class GetVinylRequest(
@Path @Transient val id: Int = 0
)

@GET("/api/vinyls")
@Returns(VinylList::class)
@Serializable
data class ListVinylsRequest(
@Query @Transient val genre: String? = null,
@Query @Transient val artist: String? = null
)

@PUT("/api/vinyls/{id}")
@Returns(Vinyl::class)
@Serializable
data class UpdateVinylRequest(
@Path @Transient val id: Int = 0,
var artist: String? = null,
var album: String? = null,
var year: Int? = null,
var genre: String? = null,
var price: Double? = null,
var stock: Int? = null
)

@DELETE("/api/vinyls/{id}")
@Returns(Unit::class)
@Serializable
data class DeleteVinylRequest(
@Path @Transient val id: Int = 0
)

// Response models
@Serializable
data class Vinyl(
val id: Int,
val artist: String,
val album: String,
val year: Int,
val genre: String,
val price: Double,
val stock: Int
)

@Serializable
data class VinylList(
val vinyls: List<Vinyl>,
val total: Int
)

Using the generated client

import dev.kolibrium.api.example.generated.VinylStoreClient
import dev.kolibrium.api.example.generated.vinylStoreApiTest
import dev.kolibrium.api.core.defaultHttpClient

// Direct client usage
suspend fun main() {
val client = VinylStoreClient(defaultHttpClient, "http://localhost:8080")

// Create a vinyl using DSL
val created = client.createVinyl {
artist = "Pink Floyd"
album = "The Dark Side of the Moon"
year = 1973
genre = "Progressive Rock"
price = 29.99
stock = 10
}
println("Created: ${created.body}")

// Get by ID
val vinyl = client.getVinyl(created.body.id)
println("Retrieved: ${vinyl.body}")

// List with filters
val rockVinyls = client.listVinyls(genre = "Progressive Rock")
println("Found ${rockVinyls.body.total} rock vinyls")

// Update
val updated = client.updateVinyl(created.body.id) {
price = 24.99
}
println("Updated price: ${updated.body.price}")

// Delete
client.deleteVinyl(created.body.id)
}

// Test usage
class VinylStoreTest {
@Test
fun `create and retrieve vinyl`() = vinylStoreApiTest {
val created = createVinyl {
artist = "Test Artist"
album = "Test Album"
year = 2024
genre = "Test"
price = 19.99
stock = 5
}

assertTrue(created.isSuccess)

val retrieved = getVinyl(created.body.id)
assertEquals(created.body, retrieved.body)

// Cleanup
deleteVinyl(created.body.id)
}

@Test
fun `test with setup and teardown`() = vinylStoreApiTest(
setUp = {
createVinyl {
artist = "Setup Artist"
album = "Setup Album"
year = 2024
genre = "Test"
price = 9.99
stock = 1
}.body.id
},
tearDown = { vinylId ->
deleteVinyl(vinylId)
}
) { vinylId ->
val response = getVinyl(vinylId)
assertEquals("Setup Artist", response.body.artist)
}
}

Best practices

Organize request models

Keep request and response models in the default models subpackage:

dev.kolibrium.api.example/
├── MyApiSpec.kt
└── models/
├── User.kt # User requests and response
├── Order.kt # Order requests and response
└── Product.kt # Product requests and response

Use descriptive names

// Good
data class GetUserByIdRequest(...)
data class ListActiveOrdersRequest(...)
data class CreateProductRequest(...)

// Avoid
data class UserRequest(...) // Ambiguous
data class GetRequest(...) // Too generic

Handle errors gracefully

val response = client.getUser(id)

when {
response.isSuccess -> handleUser(response.body)
response.isClientError -> handleNotFound()
response.isServerError -> handleServerError()
}

// Or use requireSuccess() for fail-fast
val user = client.getUser(id).requireSuccess().body

Use test harness for integration tests

@Test
fun `integration test`() = myApiTest(
setUp = { /* create test data */ },
tearDown = { /* cleanup */ }
) { testData ->
// Test with guaranteed cleanup
}