Webservices

The webservice module provides an easy way to implement RESTful services that can be consumed by clients as needed.

Hosting an API

To host an API using this framework you can define a new class that hosts your operations as dedicated functions. The signature of those functions will define how your API can be called via HTTP.

The following example shows how to define and host a service that can be used to manage an entity (books in this case).

Kotlin
import org.genhttp.engine.internal.Host

import org.genhttp.modules.apibrowsing.addRedoc
import org.genhttp.modules.layouting.Layout
import org.genhttp.modules.openapi.addOpenApi
import org.genhttp.modules.reflection.Method
import org.genhttp.modules.security.CorsPolicy
import org.genhttp.modules.webservices.ResourceMethod
import org.genhttp.modules.webservices.addService

import java.util.concurrent.ConcurrentHashMap

suspend fun main() {
    val service = Layout.create()
        .addService<BookService>("books")
        .add(CorsPolicy.permissive())
        .addOpenApi() // http://localhost:8080/openapi.json
        .addRedoc() // http://localhost:8080/redoc/

    Host.create()
        .handler(service)
        .development()
        .run()
}

data class Book(val id: Int, val title: String)

class BookService {

    private val books = ConcurrentHashMap<Int, Book>()

    // GET http://localhost:8080/books/?page=1&pageSize=20
    @ResourceMethod
    fun getBooks(page: Int = 1, pageSize: Int = 20): List<Book> =
        books.values.sortedBy { it.id }.drop(maxOf(page - 1, 0) * pageSize).take(pageSize)

    // GET http://localhost:8080/books/4711
    @ResourceMethod(path = ":id")
    fun getBook(id: Int): Book? = books[id]

    // PUT http://localhost:8080/books/
    @ResourceMethod(Method.Put)
    fun addBook(book: Book): Book = book.also { books[it.id] = it }

    // POST http://localhost:8080/books/
    @ResourceMethod(Method.Post)
    fun updateBook(book: Book): Book = book.also { books[it.id] = it }

    // DELETE http://localhost:8080/books/4711
    @ResourceMethod(Method.Delete, ":id")
    fun deleteBook(id: Int): Book? = books.remove(id)

}

Every public function annotated with @ResourceMethod becomes an operation of the service, other functions are not exposed. The service class needs a constructor without parameters, as addService<T>() creates a single instance of it that handles all requests - pass an instance with addService("books", BookService()) to construct it yourself.

As page and pageSize declare default values, clients may omit them - the defaults of the Kotlin function apply in this case.

Further Resources

The following capabilities are shared by various application frameworks: