Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Micronaut with Kotlin Coroutines
Search
Mohit S
February 03, 2021
Programming
2.3k
4
Share
Micronaut with Kotlin Coroutines
Learn how to build microservices with Micronaut.
Mohit S
February 03, 2021
More Decks by Mohit S
See All by Mohit S
Guide to Improving Compose Performance
heyitsmohit
0
320
Building Shared UIs across Platforms with Compose
heyitsmohit
1
710
Building Multiplatform Apps with Compose
heyitsmohit
2
610
Building StateFlows with Jetpack Compose
heyitsmohit
6
2k
Building Android Testing Infrastructure
heyitsmohit
1
610
Migrating to Kotlin State & Shared Flows
heyitsmohit
1
870
Using Square Workflow for Android & iOS
heyitsmohit
1
510
Building Android Infrastructure Teams at Scale
heyitsmohit
3
410
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
650
Other Decks in Programming
See All in Programming
Cloudflare で始める Data Platform
ta93abe
0
170
サーバーレスで作る、動画データ管理基盤
oyasumipants
0
200
RailsTokyo 2026#4: AI様があれば、 Hotwireの弱点は消えるか?
naofumi
2
330
Oxlintはいかにしてtsgolintのlint ruleを呼び出しているのか
syumai
0
160
Hive Metastoreを通して学ぶIceberg REST Catalog ― 仕様から実装まで
okumin
0
140
AgentCore Optimizationを始めよう!
licux
3
240
Spec Driven Development | AI Summit Vilnius
danielsogl
PRO
1
160
Agentic UI in the Frontend: Architectures with Open Standards @JAX 2026 in Mainz
manfredsteyer
PRO
0
110
Making the RBS Parser Faster
soutaro
0
720
cloudnative conference 2026 flyle
azihsoyn
0
180
AlarmKitで明後日起きれるアラームアプリを作る
trickart
0
140
ふにゃっとしない名前の付け方 〜哲学で茹で上げる、コシのあるソフトウェア設計〜
shimomura
0
120
Featured
See All Featured
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.2k
Claude Code のすすめ
schroneko
67
220k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Ruling the World: When Life Gets Gamed
codingconduct
0
230
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.2k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
180
Between Models and Reality
mayunak
4
290
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
1.9k
Test your architecture with Archunit
thirion
1
2.2k
Transcript
Mohit Sarveiya Micronaut with Kotlin Coroutines @heyitsmohit
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
What is Micronaut? • JVM Framework for building microservices •
Created by Graham Rose • Polygot - Java, Kotlin or Groovy
Benefits • Fast Startup & Small Memory • Meta programming
• Reactive • Ahead-of-Time Compilation (AOT)
Building Micronaut Project • CLI tool • Intellij • Micronaut
Launch
Project Creation CLI Tool • Download via SDKMAN • Create
scaffolding for app • Create controllers, beans or clients
Create Hello World App mn create-app
Create Hello World App mn create-app • create-cli-app • create-grpc-app
• create-messaging-app
Create Hello World App mn create-app -l kotlin • Kotlin
• Java • Groovy
Create Hello World App mn create-app -l kotlin com.learn.micronautapp Package
name
Create Hello World App micronautapp src build.gradle Gradle project D
Dockerfile
Create Hello World App micronautapp src build.gradle D Dockerfile Application.kt
Start App fun main(args: Array<String>) { build() .args(*args) .packages("com.learn") .start()
}
Start App fun main(args: Array<String>) { build() .args(*args) .packages("com.learn") .start()
}
Hello World REST API GET /greet/{name}
Hello World REST API GET /greet/{name} Hello {name}
Create Controller mn create-controller
Create Controller mn create-controller com.learn.micronautapp.Greeting
Create Controller mn create-controller com.learn.micronautapp.Greeting | Rendered controller to src/
... /GreetingController.kt | Rendered test to src/…/GreetingControllerTest.kt
Create Controller mn create-controller com.learn.micronautapp.Greeting | Rendered controller to src/
.
Testing @MicronautTest class GreetingControllerTest { }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Client("/") interface
GreetingClient { } }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Client("/") interface
GreetingClient { @Get("/greet/{name}") fun greetings(name: String): String } }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Inject lateinit
var greetingClient: GreetingClient }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Test fun
`should get greeting`() { val greeting = greetingClient.greetings("Micronaut") } }
Testing @MicronautTest class GreetingControllerTest(val embeddedServer: EmbeddedServer) { @Test fun
`should get greeting`() { val greeting = greetingClient.greetings("Micronaut") greeting shouldEqual "Greetings Micronaut" } }
Controller @Controller class GreetingController { }
Controller @Controller class GreetingController { @Get(uri=“/greet/{name}“) }
Controller @Controller class GreetingController { @Get(uri=“/greet/{name}“) }
Controller @Controller class GreetingController { @Get(uri=“/greet/{name}“) fun greet(name: String):
String { } }
Controller @Controller class GreetingController { @Get(uri=“/greet/{name}“) fun greet(name: String):
String { return "Greetings $name" } }
GET http: // localhost:8080/greet/micronaut
GET http: // localhost:8080/greet/micronaut Port
Services HTTP/1.1 200 OK content-type: text/plain
content-length: 19 connection: keep-alive Greetings micronaut GET http: // localhost:8080/greet/micronaut
Summary • Create Micronaut Project • Create controller •
Unit testing API
Coroutine Support • Suspending functions • Scopes • Dispatchers
• Flows
Coroutine Support Micronaut Core RxJava Coroutines
Using Coroutines @Controller class GreetingController { @Get(uri=“/greet/{name}“) fun greet(name:
String): String { return "Greetings $name" } }
Using Coroutines @Controller class GreetingController { @Get(uri=“/greet/{name}“) suspend fun
greet(name: String): String { return "Greetings $name" } }
Coroutines Support class Router { Flowable.defer(() -> { if
(isKotlinSuspendingFunction) { return executeKotlinSuspendingFunction( ... ); } else { ... } }) }
Coroutines Support class Router { Flowable.defer(() -> { if
(isKotlinSuspendingFunction) { return executeKotlinSuspendingFunction( ... ); } else { ... } }) }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {
} }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {
if (isKotlinFunctionReturnTypeUnit) { } else { } } }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {
if (isKotlinFunctionReturnTypeUnit) { return Completable.fromPublisher( ... ).toFlowable(); } else { } } }
Coroutines Support Publisher executeKotlinSuspendingFunction( ... ) { if (isKotlinCoroutineSuspended) {
if (isKotlinFunctionReturnTypeUnit) { return Completable.fromPublisher( ... ).toFlowable(); } else { return Publishers.fromCompletableFuture(…); } } }
Coroutine Support • Suspending functions • Dispatchers • Scope
• Flows
Using Dispatcher @Controller class GreetingController { @Get(uri=“/greet/{name}“) fun greet(name:
String): String { return "Greetings $name" } } Use a dispatcher
Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { }
Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { val
coroutineDispatcher: CoroutineDispatcher init { coroutineDispatcher = executor.asCoroutineDispatcher() } }
Using Dispatcher @Controller class GreetingController(val executor: ExecutorService) { @Get(uri="/greet/{name}")
suspend fun greetings(name: String) = withContext(coroutineDispatcher) { } }
Coroutine Support • Suspending functions • Dispatchers • Scope
• Flows
Creating Scope @Controller class GreetingController(val executor: ExecutorService) { @Get(uri="/greet/{name}")
suspend fun greetings(name: String) = coroutineScope { } }
Coroutine Support • Suspending functions • Dispatchers • Scope
• Flows
Flows Server Client
Flows GET /stream
Flows @Controller class GreetingController { @Get(uri=“/stream“) fun stream(): Flow<Int>
}
Flows @Controller class GreetingController { @Get(uri=“/stream“) fun stream(): Flow<Int>
= flowOf(1,2,3) .onEach { delay(2000) } }
GET http: // localhost:8080/stream
GET http: // localhost:8080/stream Services [ {} ]
Kotlin / kotlinx.coroutines Code ! Issues Pull Requests kotlinx.coroutines Modules
• Reactive • RxJava 2.x • RxJava 3.x
Coroutine Support Micronaut Core RxJava Coroutines
GET http: // localhost:8080/stream Services HTTP/1.1 200 OK transfer-encoding: chunked
[ 1, 2, 3 ]
Coroutine Support class FlowConverterRegistrar { @Override public void register( ...
) { addConverter( ... , flow -> Flowable.fromPublisher(ReactiveFlowKt.asPublisher(flow)) ); } }
Coroutine Support class FlowConverterRegistrar { @Override public void register( ...
) { addConverter( ... , flow -> Flowable.fromPublisher(ReactiveFlowKt.asPublisher(flow)) ); } }
Coroutine Support • Suspending functions • Dispatchers • Scope
• Flows
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
Task REST API GET /tasks/list
Task REST API GET /tasks/list GET /tasks/{taskId}
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks {
“id”: taskId “description”: “ ” }
Task REST API GET /tasks/list GET /tasks/{taskId} PUT /tasks {
“id”: taskId “description”: “ ” } POST /tasks
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access Server DB
Data Access Server DB Hibernate
Data Access data class Task( val id: Long? = null,
val description: String = "" )
Data Access @Entity data class Task( )
Data Access @Entity @Table(name = "task") data class Task( )
Data Access data class Task( @Id val id: Long? =
null, )
Data Access data class Task( @Id @GeneratedValue(strategy = GenerationType.AUTO) val
id: Long? = null, )
Data Access data class Task( @Column(name = "description", nullable =
false, unique = true) val description: String = "" )
Data Access DB Task Repo
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access class TasksRepository { }
Data Access class TasksRepository(val entityManager: EntityManager) { }
Data Access class TasksRepository(val entityManager: EntityManager) { fun findById(taskId: Long):
Task { } }
Data Access class TasksRepository(val entityManager: EntityManager) { fun findById(taskId: Long):
Task { return entityManager.find(Task :: class.java, taskId) } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access class TasksRepository(val entityManager: EntityManager) { fun save(description: String):
Task { } }
Data Access class TasksRepository(val entityManager: EntityManager) { fun save(description: String):
Task { val task = Task(description = description) } }
Data Access class TasksRepository(val entityManager: EntityManager) { fun save(description: String):
Task { val task = Task(description = description) entityManager.persist(task) return task } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,
description: String): Int { } }
Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,
description: String): Int { val query = "UPDATE Task t SET description = :description where taskId = :taskId" } }
Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,
description: String): Int { return entityManager.createQuery(sql) }
Data Access class TasksRepository(val entityManager: EntityManager) { fun update(taskId: Long,
description: String): Int { return entityManager.createQuery(sql) .setParameter(”taskId", taskId) .setParameter(“description", description) .executeUpdate() }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access class TasksRepository(val entityManager: EntityManager) { fun delete(taskId: Long)
{ val task = findById(taskId) task ?. let { entityManager.remove(it) } } }
Data Access DB Task Repo Controller
Controller class TaskController { }
Controller class TaskController(private val taskRepository: TaskRepository) { }
Controller @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { }
Controller @ExecuteOn(TaskExecutors.IO) @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Controller @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Get("/{taskId}") fun
getTask(taskId: Long): Task? { return taskRepository.findById(taskId) } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Data Access data class TaskSaveCommand( val description: String )
Data Access Controller Client
Data Access Controller Client “Task 1”
Data Access Controller Client “ ”
Data Access Controller Client Validation
Data Access data class TaskSaveCommand( @field:NotBlank val description: String )
Data Access @Introspected data class TaskSaveCommand( @field:NotBlank val description: String
)
Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post
suspend fun save(@Body command: TaskSaveCommand) { } }
Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post
suspend fun save(@Body @Valid command: TaskSaveCommand) { } }
Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post
suspend fun save(@Body @Valid command: TaskSaveCommand) { val task = taskRepository.save(command.description) } }
HTTP Response Controller Client 201 OK
Micronaut HTTP • Set status code • Update headers •
Send errors
Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Post
suspend fun save(@Body @Valid command: TaskSaveCommand) { val task = taskRepository.save(command.description) return HttpResponse.created(task) } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
HTTP Response Controller Client Delete taskID
HTTP Response Controller Client No content
Data Access @Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}")
fun delete(taskId: Long): HttpResponse<Task> { taskRepository.delete(taskId) return HttpResponse.noContent() } }
Task REST API GET /tasks/list GET /tasks/{taskId} POST /tasks PUT
/tasks DELETE /tasks/taskId
Hibernate In-memory Server DB
@Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}") fun delete(taskId:
Long): HttpResponse<Task> { taskRepository.delete(taskId) return HttpResponse.noContent() } } Services org.hibernate.dialect.Dialect - Using dialect: org.hibernate.dialect.H2Dialect
Tasks Project taskapp src build.gradle Configure app D Dockerfile application.yml
Configure Database micronaut: application: name: complete datasources: default: url: {JDBC_URL:`
... `} username: ${JDBC_USER:sa} password: ${JDBC_PASSWORD:""} driverClassName: ${JDBC_DRIVER:org.h2.Driver}
POST /tasks { “description”: “task1” }
HTTP/1.1 201 OK { "id": 1, "description": "task1" } POST
/tasks
GET /tasks/list [ { "id": 1, "description": "task1" } ]
Create Client Server Client
CLI Client • Simple client to test APIs • Create
Runnable
CLI App mn create-cli-app
CLI App mn create-cli-app -l kotlin com.learn.cliapp
interface TaskClient { @Get("/task/list") fun getTasks(): List<Task> }
CLI App
interface TaskClient { @Get("/task/list") fun getTasks(): List<Task> @Post("/task")
fun save(description: String): Task } CLI App
class TaskclientCommand : Runnable { @Inject lateinit var taskClient: TaskClient
} CLI App
class TaskclientCommand : Runnable { fun run() { taskClient.getTasks() .forEach
{ println(it) } } CLI App
Summary • Setup Controller for REST API • HTTP Requests
& Responses • Data Access • Create Client
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
Controller @Controller("/tasks") class TaskController(val taskRepository: TaskRepository) { @Get(“/list") fun list():
List<Task> { } } Exception
Circuit Breaker • # of Retries • Delay
@CircuitBreaker(delay="1s", attempts = "5") interface TaskClient { } CLI
App
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
Load Balancing Server Client Server Server
Tasks Project taskapp src build.gradle Configure app D Dockerfile application.yml
Tasks Project consul: client: registration: enabled: true default-zone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"
@Controller("/tasks") class TaskController(private val taskRepository: TaskRepository) { @Delete("/{taskId}") fun delete(taskId:
Long): HttpResponse<Task> { taskRepository.delete(taskId) return HttpResponse.noContent() } } Services Registered service with Consul io.micronaut.runtime.Micronaut - Server Running: http: // localhost:44004
Load Balancing Server Client Server Server
Micronaut with Kotlin Coroutines • Building REST API • Coroutines
• Data Access • Circuit breakers • Load Balancing
Resources • Introduction to Micronaut • Graeme Rocher •
Talk on coroutines! https: // codingwithmohit.com/talks/
Thank You! www.codingwithmohit.com @heyitsmohit