implementation("io.micronaut.kotlin:micronaut-kotlin-runtime")
Table of Contents
Micronaut Kotlin Integrations
Includes extensions to Micronaut for Kotlin
Version: 4.6.1-SNAPSHOT
1 Introduction
This project provides various extensions and improvements to the Micronaut and Kotlin experience.
2 Release History
For this project, you can find a list of releases (with release notes) here:
3 Kotlin Runtime Support
The micronaut-kotlin-runtime
dependency adds the following features:
-
Support for defining configuration with config4k.
-
A runtime dependency on
jackson-module-kotlin
To enable the above features add the dependency to your build:
<dependency>
<groupId>io.micronaut.kotlin</groupId>
<artifactId>micronaut-kotlin-runtime</artifactId>
</dependency>
Config4k Support
3.1 Config4k
Config4k is a type safe configuration format for Kotlin based on HOCON (Human-Optimized Config Object Notation). Configuration files are defined using the conf
extension. The following an example configuration file:
src/main/resources/application.conf
micronaut { server { port = 8081 } } test-property = "bad-value" custom { user = ${USER} }
4 Ktor Support
Ktor is a Kotlin framework for building connected applications. The micronaut-ktor
module includes support for using Ktor as the server instead of Micronaut’s native HTTP server.
This allows users familiar with Ktor to use Micronaut features such as Dependency Injection, AOP, configuration management and so on.
implementation("io.micronaut.kotlin:micronaut-ktor")
<dependency>
<groupId>io.micronaut.kotlin</groupId>
<artifactId>micronaut-ktor</artifactId>
</dependency>
See the Example application which demonstrates how to setup a Micronaut Ktor application |
4.1 The KtorApplication class
The entry point for a Micronaut Ktor application is an Application
class. An example class can be seen below:
Application
classimport io.ktor.server.netty.NettyApplicationEngine
import io.micronaut.ktor.KtorApplication
import io.micronaut.ktor.runApplication
import jakarta.inject.Singleton
import org.slf4j.LoggerFactory
@Singleton
class Application : KtorApplication<NettyApplicationEngine.Configuration>({ (1)
applicationEngineEnvironment { (2)
log = LoggerFactory.getLogger(Application::class.java)
}
applicationEngine { (3)
workerGroupSize = 10
}
})
fun main(args: Array<String>) { (4)
runApplication(args)
}
1 | The Application class extends io.micronaut.ktor.KtorApplication and provides the server type (in this case Netty). |
2 | You can optionally configure the ApplicationEngineEnvironment. |
3 | You can optionally configure the ApplicationEngine . In this case NettyApplicationEngine instance’s workerGroupSize is set to 10 |
4 | A main method is defined for running the application within a runnable JAR |
4.2 Defining Ktor Modules
To define Ktor modules you can create classes that subclass io.micronaut.ktor.KtorApplicationBuilder
to install features or io.micronaut.ktor.KtorRoutingBuilder
to configure routes.
For example, the following will install the Jackson feature (when ktor-jackson
is on the classpath):
import com.fasterxml.jackson.databind.SerializationFeature
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.jackson.jackson
import io.micronaut.ktor.KtorApplicationBuilder
import jakarta.inject.Singleton
@Singleton
class GreetingConfiguration : KtorApplicationBuilder({ (1)
install(ContentNegotiation) { (2)
jackson {
enable(SerializationFeature.INDENT_OUTPUT)
}
}
})
1 | The class subclasses KtorApplicationBuilder . |
2 | The ContentNegotiation feature is installed and Jackson configured. |
To build application routes you can use the KtorRoutingBuilder
:
import io.ktor.server.application.call
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.routing.*
import io.micronaut.ktor.KtorRoutingBuilder
import jakarta.inject.Singleton
@Singleton
class GreetingRoutes(private val greetingService: GreetingService) : KtorRoutingBuilder({ (1)
get("/") {
call.respond(greetingService.greet())
}
post("/") {
val name = call.receive<CustomGreetingRequest>().name
call.respond(greetingService.greet(name))
}
})
data class CustomGreetingRequest(val name: String)
1 | The class subclasses KtorRoutingBuilder and uses dependency injection to reference GreetingService . |
2 | The routing DSL is used to build the application routes. |
5 Kotlin Extension Functions
The micronaut-kotlin-extension-functions
dependency adds a variety of convenience functions to make using
micronaut with kotlin more user-friendly. For example, reified type parameters
help alleviate the need for using ::class.java
in many places where it would otherwise be required.
implementation("io.micronaut.kotlin:micronaut-kotlin-extension-functions")
<dependency>
<groupId>io.micronaut.kotlin</groupId>
<artifactId>micronaut-kotlin-extension-functions</artifactId>
</dependency>
See the guide for Using Kotlin Extension Functions in the Micronaut Framework to learn more. |
You will need to import the functions like this: import io.micronaut.kotlin.FUNCTION_NAME
|
Do be aware when defining extension functions in this project or your own, an extension that shadows a member function may have unexpected behavior, and does not throw a compiler error but rather warns. See this Kotlin discussion topic for the latest information. |
5.1 Context Extensions
5.2 HTTP Extensions
BlockingHttpClient Extensions
Single Response Extensions
// Compare exchange usage
val exchangeOneConventional: HttpResponse<Hero> = client.exchange(HttpRequest.GET<Any>("/heroes/any"), Argument.of(Hero::class.java))
val exchangeOneReified: HttpResponse<Hero> = client.exchangeObject<Hero>(HttpRequest.GET("/heroes/any"))
// Compare retrieve usage
val retrieveOneConventional: Hero = client.retrieve(HttpRequest.GET<Any>("/heroes/any"), Argument.of(Hero::class.java))
val retrieveOneReified: Hero = client.retrieveObject<Hero>(HttpRequest.GET("/heroes/any"))
List Response Extensions
// Compare exchange usage
val exchangeListConventional: HttpResponse<MutableList<Hero>> = client.exchange(HttpRequest.GET<Any>("/heroes/list"), Argument.listOf(Hero::class.java))
val exchangeListReified: HttpResponse<List<Hero>> = client.exchangeList<Hero>(HttpRequest.GET("/heroes/list"))
// Compare retrieve usage
val retrieveListConventional: MutableList<Hero> = client.retrieve(HttpRequest.GET<Any>("/heroes/list"), Argument.listOf(Hero::class.java))
val retrieveListReified: List<Hero> = client.retrieveList<Hero>(HttpRequest.GET("/heroes/list"))
HttpMessage Extensions
// Compare body member usage (Not exceptional path)
val exchangeOneConventionalBody: Hero? = exchangeOneConventional.body.getOrNull()
val exchangeOneReifiedBody: Hero? = exchangeOneReified.bodyOrNull
// Compare getBody() usage (Exceptional path)
val exchangeOneConventionalCustomException: HttpClientResponseException = assertThrows<HttpClientResponseException> {
client.exchange(HttpRequest.GET<Any>("/heroes/conflict"), Argument.of(Hero::class.java))
}
val exchangeOneReifiedCustomException: HttpClientResponseException = assertThrows<HttpClientResponseException> {
client.exchangeObject<Hero>(HttpRequest.GET("/heroes/conflict"))
}
val exchangeOneConventionalCustomExceptionBody: HeroJsonError? = exchangeOneConventionalCustomException.response.getBody(HeroJsonError::class.java).getOrNull()
val exchangeOneReifiedCustomExceptionBody: HeroJsonError? = exchangeOneReifiedCustomException.response.getBodyObject<HeroJsonError>()
5.3 Inject Extensions
5.4 Runtime Extensions
object Application
fun main(args: Array<String>) {
startApplication<Application>(*args) {
packages("org.example.app")
mapError<RuntimeException> { 500 }
}
}
5.5 Scheduling Extensions
6 Breaking Changes
This section documents breaking changes for Kotlin and Ktor versions:
Micronaut Kotlin 4.6.1-SNAPSHOT
Micronaut Kotlin’s Ktor
module is based on Ktor 2.x, which has breaking changes from Ktor 1.6.x used by previous versions of Micronaut Kotlin.
For more information, refer to the following Ktor guide:
7 Repository
You can find the source code of this project in this repository: