Skip to content

AcapellaSoft/Aconite

Repository files navigation

Aconite

Type-safe HTTP client/server framework for Kotlin inspired by Retrofit.

Download

Dependencies for maven:

<dependency>
  <groupId>io.aconite</groupId>
  <artifactId>aconite-core</artifactId>
  <version>0.5.2</version>
  <type>pom</type>
</dependency>
<dependency>
  <groupId>io.aconite</groupId>
  <artifactId>aconite-server</artifactId>
  <version>0.5.2</version>
  <type>pom</type>
</dependency>
<dependency>
  <groupId>io.aconite</groupId>
  <artifactId>aconite-client</artifactId>
  <version>0.5.2</version>
  <type>pom</type>
</dependency>

or for gradle:

buildscript {
    ext.aconite_version = '0.5.2'
}

dependencies {
    compile group: 'io.aconite', name: 'aconite-core', version: "$aconite_version"
    compile group: 'io.aconite', name: 'aconite-server', version: "$aconite_version"
    compile group: 'io.aconite', name: 'aconite-client', version: "$aconite_version"
}

Introduction

  1. Declare your HTTP API as an interface (similar to retrofit):
interface HelloApi {
    @POST("/hello") suspend fun hello(@Query name: String): String
}
  1. Create an interface implementation for the server:
class HelloImpl : HelloApi {
    override suspend fun hello(name: String) = "Hello, $name!"
}
  1. Create and run the server using default vertx handler
val server = serverPipeline {
    install(AconiteServer) {
        register(HelloImpl(), HelloApi::class)
    }
}
VertxHandler.runServer(server, 8080)
  1. Generate an interface implementation for the client:
val client = AconiteClient(VertxHttpClient())
val api = client.create<HelloApi>()["http://localhost:8080"]
  1. Make a call:
val response = api.hello("World")
println(response) // prints "Hello, World!"
  1. Test the server by curl:
$ curl -XPOST http://localhost:8080/hello?name=World
Hello, World!

Declaring HTTP interfaces

HTTP interface is a simple Kotlin interface with HTTP annotations on functions and arguments. To declare function, that are going to be an HTTP request, you need to select a method annotation from this list:

  • DELETE(url)
  • GET(url)
  • HEAD(url)
  • OPTIONS(url)
  • PATCH(url)
  • POST(url)
  • PUT(url)

or use HTTP(method, url) to support custom HTTP methods. Also aconite supports inner interfaces with MODULE(url) annotation, that can be used to split your API into small independent parts. All arguments of the function must be annotated with one of the following annotations:

  • Body
  • Header(name)
  • Path(name)
  • Query(name)

By default, the name field of the annotation is equal to the function's argument name.

Here are some examples of complex interfaces:

data class User(val firstName: String, val lastName: String)
data class Post(val content: String, val createdAt: Date)

interface RootApi {
    @MODULE("/users/{id}") suspend fun user(@Path id: UUID): UserApi
    @MODULE("/posts/{id}") suspend fun post(@Path id: UUID): PostApi
}

interface UserApi {
    @GET suspend fun get(): User
    @PATCH suspend fun update(@Query firstName: String, @Query lastName: String)
    @GET("/posts") suspend fun getAllPosts(): Map<UUID, Post>
}

interface PostApi {
    @GET suspend fun get(): Post
    @PUT suspend fun put(@Query author: UUID, @Body content: String): Post
}

data class First(val a: Int, val b: Long)
data class Second(val c: String, val d: UUID)

interface GenericExample {
    @MODULE("/first") suspend fun first(): GenericApi<First>
    @MODULE("/second") suspend fun second(): GenericApi<Second>
}

interface GenericApi<T> {
    @GET("/{key}") suspend fun get(@Path key: String): T
    @PUT("/{key}") suspend fun put(@Path key: String, @Body value: T)
}

More examples can be found here.

Build

Aconite using gradle build tool. To build and run tests, use following command:

./gradlew clean shadowjar test