Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin_for_server-side_application_development.pdf

 Kotlin_for_server-side_application_development.pdf

Anton Arhipov

December 02, 2022
Tweet

More Decks by Anton Arhipov

Other Decks in Programming

Transcript

  1. 2011 - alternative JVM language Then - Android, JavaScript, LLVM,

    WASM….. 2017 - O ffi cially supported for Android
  2. 2011 - alternative JVM language Multi-Platform Projects Then - Android,

    JavaScript, LLVM, WASM….. 2017 - O ffi cially supported for Android
  3. https://youtu.be/7YJyPXjLdug In Kotlin, you can build a data model that’s

    going to match exactly how your GraphQL schema looks like
  4. Extreme code reuse with Kotlin Multiplatform Brave new world with

    microservices and happier developers! Expressiveness and safety for building GraphQL APIs (with Java interoperability!)
  5. Extreme code reuse with Kotlin Multiplatform Brave new world with

    microservices and happier developers! Expressiveness and safety for building GraphQL APIs (with Java interoperability!) High-load, lock-free software with Kotlin Coroutines
  6. import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication … @SpringBootApplication class DemoApplication fun main(args:

    Array<String>) { runApplication<DemoApplication>(*args) } @RestController class MessageResource(val service: MessageService) { @GetMapping fun index(): List<Message> = service.findMessages() @PostMapping fun post(@RequestBody message: Message) { service.post(message) } }
  7. @Service class MessageService(val db: MessageRepository) { fun findMessages(): List<Message> =

    db.findMessages() fun post(message: Message){ db.save(message) } } interface MessageRepository : CrudRepository<Message, String>{ @Query("select * from messages") fun findMessages(): List<Message> } @Table("MESSAGES") data class Message(@Id val id: String?, val text: String)
  8. @Service class MessageService(val db: MessageRepository) { fun findMessages(): List<Message> =

    db.findMessages() fun post(message: Message){ db.save(message) } } interface MessageRepository : CrudRepository<Message, String> { @Query("select * from messages") fun findMessages(): List<Message> } @Table("MESSAGES") data class Message(@Id val id: String?, val text: String)
  9. @Service class MessageService(val db: MessageRepository) { fun findMessages(): List<Message> =

    db.findMessages() fun post(message: Message){ db.save(message) } } interface MessageRepository : CrudRepository<Message, String>{ @Query("select * from messages") fun findMessages(): List<Message> } @Table("MESSAGES") data class Message(@Id val id: String?, val text: String)
  10. @Service class MessageService(val db: MessageRepository) { fun findMessages(): List<Message> =

    db.findMessages() fun post(message: Message){ db.save(message) } } interface MessageRepository : CrudRepository<Message, String>{ @Query("select * from messages") fun findMessages(): List<Message> } @Table("MESSAGES") data class Message(@Id val id: String?, val text: String) Not to be used with JPA!
  11. Important for starters * Kotlin classes are fi nal by

    default * Compiler plugins to the rescue!
  12. Important for starters * Kotlin classes are fi nal by

    default * Compiler plugins to the rescue! * Data classes don’t play well with JPA
  13. Important for starters * Kotlin classes are fi nal by

    default * Compiler plugins to the rescue! * Data classes don’t play well with JPA * Use usual classes with JPA
  14. * Kotlin classes are fi nal by default * Compiler

    plugins to the rescue! * Data classes don’t play well with JPA * Use usual classes with JPA Important for starters * Use start.spring.io to generate con fi gs
  15. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } }
  16. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } }
  17. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id)
  18. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id)
  19. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id)
  20. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", RowMapper { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id) RowMapper is a functional interface
  21. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id) “SAM conversion” is performed by the compiler
  22. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }, id)
  23. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id, { rs, _ -> Message(rs.getString("id"), rs.getString("text")) })
  24. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id, { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }) vararg is not at the last position
  25. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id, { rs, _ -> Message(rs.getString("id"), rs.getString("text")) }) Lambda expression is at the last position
  26. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } }
  27. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } Passing trailing lambda as a parameter to the function
  28. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } }
  29. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } } This is an extension function For the existing Java class in the Spring framework
  30. @Service class MessageService(val db: JdbcTemplate) { fun findMessages(): List<Message> =

    db.query("select * from messages") { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun findMessageById(id: String): List<Message> = db.query("select * from messages where id = ?", id) { rs, _ - > Message(rs.getString("id"), rs.getString("text")) } fun post(message: Message){ db.update("insert into messages values ( ?, ? )", message.id ?: message.text.uuid(), message.text) } }
  31. fun String.uuid() = UUID.nameUUIDFromBytes(encodeToByteArray()).toString() fun applyAction(vararg s: String, action: (String)

    -> Unit) { s.forEach(action) } fun main() { applyAction("hello", "bye") { s: String -> println(s.uuid()) } }
  32. fun String.uuid() = UUID.nameUUIDFromBytes(encodeToByteArray()).toString() fun applyAction(vararg s: String, action: (String)

    -> Unit) { s.forEach(action) } fun main() { applyAction("hello", "bye") { s: String -> println(s.uuid()) } } Note to self: open the IDE
  33. class HtmlController(val messageService: MessageService) { @GetMapping("/") fun index(model: Model): String

    { val messages = messageService.latest() model.asMap()["messages"] = messages model.asMap()["lastMessageId"] = messages.lastOrNull() ? . id ? : "" return "chat" } }
  34. class HtmlController(val messageService: MessageService) { @GetMapping("/") fun index(model: Model): String

    { val messages = messageService.latest() model.asMap()["messages"] = messages model.asMap()["lastMessageId"] = messages.lastOrNull() ? . id ? : "" return "chat" } }
  35. class HtmlController(val messageService: MessageService) { @GetMapping("/") fun index(model: Model): String

    { val messages = messageService.latest() model.asMap()["messages"] = messages model.asMap()["lastMessageId"] = messages.lastOrNull() ? . id ? : "" return "chat" } }
  36. class HtmlController(val messageService: MessageService) { @GetMapping("/") fun index(model: Model): String

    { val messages = messageService.latest() model["messages"] = messages model["lastMessageId"] = messages.lastOrNull() ?. id ?: "" return "chat" } }
  37. val beans = beans { bean { CommandLineRunner { println("start

    data initialization .. . ") val repository = ref<MessageRepository>() repository.save(Message(text = "this is the first message!")) repository.save(Message(text = "this is the second")) } } } @SpringBootApplication class DemoApplication fun main(args: Array<String>) { runApplication<DemoApplication>(*args) { this.addInitializers(beans) } }
  38. @SpringBootApplication class DemoApplication fun main(args: Array<String>) { runApplication<DemoApplication>(*args) { this.addInitializers(beans)

    } } val beans = beans { bean { CommandLineRunner { println("start data initialization .. . ") val repository = ref<MessageRepository>() repository.save(Message(text = "this is the first message!")) repository.save(Message(text = "this is the second } } }
  39. Trailing lambdas + (extension) functions + named parameters + lambda

    w/ receiver = DSL fun foo(f: () -> Unit) {} fun bar(f: () -> Unit) {} fun baz(f: () -> Unit) {} fun blah(id: Int, f: Klazz.() -> Unit) {} class Klazz(var text: String) fun Klazz.hello() : String = "Hello"
  40. val person: Person? = getPerson() println(person.firstName) fun getPerson(): Person? {…}

    data class Person( var firstName: String, // … ) Can return null
  41. val person: Person? = getPerson() println(person.firstName) fun getPerson(): Person? {…}

    data class Person( var firstName: String, // … ) Can be null
  42. val person: Person? = getPerson() println(person ?. firstName) fun getPerson():

    Person? {…} data class Person( var firstName: String, // … ) Null-safe dereferencing
  43. val person: Person? = getPerson() println(person !! .firstName) fun getPerson():

    Person? {…} data class Person( var firstName: String, // … ) “I know what I’m doing”
  44. Kotlin features you have seen now Type inference Inline functions

    Data classes Rei fi ed generics Top-level functions Named arguments Operator overloading Trailing lambdas Extension functions Functional literal with receiver Nullable types Support for JSR-305
  45. import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.*

    fun main() { embeddedServer(Netty, port = 8080) { routing { get("/") { call.respondText("Hello World!") } } }.start(wait = true) }
  46. import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.*

    fun main() { embeddedServer(Netty, port = 8080) { routing { get("/") { call.respondText("Hello World!") } } }.start(wait = true) } Start an embedded server with Netty engine on port 8080
  47. import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.*

    fun main() { embeddedServer(Netty, port = 8080) { routing { get("/") { call.respondText("Hello World!") } } }.start(wait = true) } Other options: Tomcat, Jetty, CIO
  48. import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.*

    fun main() { embeddedServer(Netty, port = 8080) { routing { get("/") { call.respondText("Hello World!") } } }.start(wait = true) } Fixed port
  49. import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.*

    fun main() { embeddedServer(Netty, port = 0) { routing { get("/") { call.respondText("Hello World!") } } }.start(wait = true) } Random port
  50. import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.*

    fun main() { embeddedServer(Netty, port = 0) { routing { get("/") { call.respondText("Hello World!") } } }.start(wait = true) } Adding routes
  51. import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.*

    fun main() { embeddedServer(Netty, port = 0) { routing { get("/") { call.respondText("Hello World!") } } }.start(wait = true) } Request mapping
  52. import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.*

    fun main() { embeddedServer(Netty, port = 0) { routing { get("/") { call.respondText("Hello World!") } } }.start(wait = true) } Write response
  53. import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.application.* import io.ktor.response.* import io.ktor.routing.*

    fun main() { embeddedServer(Netty, port = 8080) { routing { get("/") { call.respondText("Hello World!") } } }.start(wait = true) } dependencies { implementation("io.ktor:ktor-server-core:$ktor_version") implementation("io.ktor:ktor-server-netty:$ktor_version") implementation("ch.qos.logback:logback-classic:$logback_version") testImplementation("io.ktor:ktor-server-tests:$ktor_version") testImplementation("org.jetbrains.kotlin:kotlin-test:$kotlin_version") } build.gradle.kts:
  54. How can I do … with Ktor? Logging Authentication Testing

    Serialization Monitoring JDBC / jdbi / Exposed / SQLDelight / jOOQ SLF4J, Logback, Log4j Jackson, Gson, kotlinx.serialization Views kotlinx.html, Velocity, Freemarker, Thymeleaf kotlin-test, JUnit, Testconainers Prometeus, OpenTracing Ktor * Persistence