Inspired by Spraypaint.JS
A Kotlin library for interacting with JSONAPI-compliant APIs.
Add the following to the common dependencies of your build.gradle.kts
file:
implementation("it.maicol07.spraypaintkt:core:$latest_version")
You can find snapshots on Github Packages.
To use them, you need to add the following to your settings.gradle.kts
file in the dependencyResolutionManagement
block:
repositories {
maven {
url = uri("https://maven.pkg.github.com/maicol07/spraypaintkt")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME")
password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN")
}
}
}
Then, you have to add your username and a personal access token to your local.properties
file:
gpr.user=USERNAME
gpr.key=TOKEN
Note
More info can be found here
You can use any HTTP client you want, as long as you wrap it in a HttpClient
implementation.
The library provides a Ktor integration (see Ktor Integration)
class HttpClientImpl : HttpClient {
// ...
}
Since Kotlin Multiplatform doesn't support reflection, you need create a class that implements ModelGenerator
and returns the new instance of the model.
The library provides a Koin integration (see Koin Integration)
class ModelGeneratorImpl : ModelGenerator {
override fun <R: Resource> generate(clazz: KClass<R>): R {
// your own implementation
}
}
You'll now need to create a Client instance, which will be used to interact with the API:
val client = Client(
httpClient = HttpClientImpl(),
modelGenerator = ModelGeneratorImpl(),
baseUrl = "https://api.example.com"
)
You can create your own models by extending the Resource
class:
class User : Resource() {
}
You can add attributes to your model by appending the by attributes
delegate to the property:
class User : Resource() {
val name: String by attributes
val email: String by attributes
}
If you wish to use a different property name than the one returned by the API, you can use the attribute
delegate:
class User : Resource() {
val name: String by attribute("full_name")
}
You can add relationships to your model by appending the by relationships
delegate to the property:
class User : Resource() {
val posts: List<Post> by relationships
val comments: List<Comment> by relationships
val membership: Membership by relationships
}
To-One relationships are automatically converted to the correct type, while To-Many relationships are always converted to a List
(with the model type in generics).
Important
You need to register the related models in the Client
instance and include them in the request to be able to resolve the relationships (see below).
If you wish to use a different property name than the one returned by the API, you can use the relationship
and hasManyRelationship
delegate:
class User : Resource() {
val posts: List<Post> by hasManyrelationship("user_posts")
val comments: List<Comment> by hasManyrelationship("user_comments")
val membership: Membership by relationship("user_membership")
}
You can customize the model by setting the type
and endpoint
properties when extending the Resource
class:
class User : Resource(type = "users", endpoint = "models/users") {
}
To be able to resolve the relationships, you need to register the models in the Client
instance:
client.registerResource<User>()
// You can also register a custom type instead of the model one
client.registerResource<User>("users")
You can query the API using the Client
instance:
// Find the user with ID = 1
val response = client.find<User>(1)
val user = response.data
// Fetch all the users
val response = client.all<User>()
val users = response.data
// Fetch the first user in the response
val response = client.first<User>()
val user = response.data
You can filter the results using the where
method:
val response = client.where("name", "John").all<User>()
val user = response.data
// You can also use the `where` method multiple times
val response = client.where("name", "John").where("email", "john@doe.com").all<User>()
val user = response.data
You can sort the results using the order
method:
val response = client.order("name", SortDirection.DESC).all<User>()
val user = response.data
You can paginate the results using the per
and page
methods:
val response = client.per(10).page(2).all<User>()
val user = response.data
If you're using offset-based pagination, you have to change the pagination strategy when creating the client and use the offset
and limit
methods:
val client = Client(
httpClient = HttpClientImpl(),
modelGenerator = ModelGeneratorImpl(),
baseUrl = "https://api.example.com",
paginationStrategy = OffsetBasedPaginationStrategy()
)
val response = client.offset(10).limit(50).all<User>()
val user = response.data
You can include the relationships using the includes
method:
val response = client.includes("posts", "comments").all<User>()
val user = response.data
Caution
Only if you include the relationships in the request, the library will be able to resolve them in your model.
You can create a new resource creating a new resource, filling all the attributes and relationships and then calling the save
method:
val user = User()
user.name = "John"
user.email = "john@doe.com"
val result = client.save(user)
if (result) {
// The resource has been created
}
You can update a resource by modifying the attributes and relationships and then calling the save
method:
val user = client.find<User>(1).data
user.name = "John Doe"
val result = client.save(user)
if (result) {
// The resource has been updated
}
To delete a resource, you can use the destroy
method:
val result = client.destroy(user)
if (result) {
// The resource has been deleted
}
The library provides a JsonApiException
class, which is thrown when the JSONAPI server returns an error.
You can catch it and handle it as you want:
try {
val user = client.find<User>(1).data
} catch (e: JsonApiException) {
// Handle the exception
println(e.errors)
}
The library provides a Ktor integration, which allows you to use the HttpClient
implementation provided by Ktor.
Add the following to the common dependencies of your build.gradle.kts
file:
implementation("it.maicol07.spraypaintkt:ktor-integration:$latest_version")
You can now create a Client
instance using the KtorHttpClient
:
val client = Client(
httpClient = KtorHttpClient(),
modelGenerator = ModelGeneratorImpl(),
baseUrl = "https://api.example.com"
)
You can configure the Ktor client by passing a HttpClientConfig
instance to the KtorHttpClient
constructor:
val client = Client(
httpClient = KtorHttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
},
modelGenerator = ModelGeneratorImpl(),
baseUrl = "https://api.example.com"
)
You can use a custom Ktor client by passing it to the KtorHttpClient
constructor:
val client = Client(
httpClient = KtorHttpClient(
client = HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
}
),
modelGenerator = ModelGeneratorImpl(),
baseUrl = "https://api.example.com"
)
The library provides a Koin integration, which allows you to use the ModelGenerator
implementation provided by Koin.
Models will be resolved using the Koin container, but you need to add them manually in a module.
Add the following to the common dependencies of your build.gradle.kts
file:
implementation("it.maicol07.spraypaintkt:koin-integration:$latest_version")
You can now create a Client
instance using the KoinModelGenerator
:
val client = Client(
httpClient = HttpClientImpl(),
modelGenerator = KoinModelGenerator(),
baseUrl = "https://api.example.com"
)
You need to add the models to the Koin container:
val modelsModule = module {
single { User() }
single { Post() }
single { Comment() }
single { Membership() }
}