Kotlin と Micronaut で TODO アプリを作ってみた
はじめに
Micronaut を使って簡単な Web アプリを作ってみました。Micronaut に関する日本語の情報はまだまだ少ないので、誰かの助けになればと思い記事を公開することにしました。
環境
- OS: Windows 10
- IDE: IntelliJ IDEA 2022.2.1 (Ultimate Edition)
- 言語: Kotlin 1.6.21
- フレームワーク: Micronaut (v3.5.5)
Micronaut とは
JVM ベースのフルスタックフレームワークです。Java, Kotlin, Groovy に対応しています。詳しくは公式サイトをご覧ください。
プロジェクトの作成
まずは IntelliJ IDEA を起動して、新規プロジェクトを作成します。Ultimate Edition であれば Micronaut を選べるので、それを選択します。そうでない場合、Micronaut Launch から同様のことができると思うのでそちらで行ってください。
名前を todo_micronaut
(好きな名前で OK) にし、言語を Kotlin、ビルドシステムを Gradle にします。テストは今回使いません。残りはそのままにします。
次へを選択し、機能を追加します。
- Micronaut Data JPA
- H2 Database
- Thymeleaf Views
を選択します。
作成をクリックし、しばらく待つと準備完了です。
テンプレートの作成
まず resources フォルダの中に views というフォルダを作り、その中に index.html というファイルを作成します。中身を次のようにします。
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>TODO</title>
</head>
<body>
<ul>
<li th:each="data : ${dataList}">
<span th:text="${data.text}">テキスト</span>
<form action="/delete" method="post">
<input type="hidden" name="id" th:value="${data.id}">
<button type="submit">削除</button>
</form>
</li>
</ul>
<form action="/add" method="post">
<label for="text">入力してね:</label><input id="text" name="text">
<button type="submit">追加</button>
</form>
</body>
</html>
th: とついているところはテンプレートエンジンである Thymeleaf の機能です。例えば th:each はリストの中身を列挙して何かを行うことを表します。
試しに素の状態で HTML ファイルを開いてみると次のような表示になります。
「テキスト」と書かれている部分が、アプリを実行しているときにはいい感じに置き換わる予定です。まだボタンを押しても機能しません。
エンティティとリポジトリ
ボタンを押したときの動作をプログラムする前に、データベースとやり取りする部分を書いていきます。
Application.kt と同じフォルダに、Entity.kt というファイルを作成します。
package com.example
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
@Entity
data class TodoEntry(@Id @GeneratedValue var id: Long = 0, var text: String = "")
Entity アノテーションをつけます。デフォルト値がないとエラーになるので設定しておきます。
次に TodoRepository.kt というファイルを作ります。
package com.example
import io.micronaut.data.annotation.Repository
import io.micronaut.data.repository.CrudRepository
@Repository
interface TodoRepository : CrudRepository<TodoEntry, Long>
Repository アノテーションをつけます。CrudRepository を継承します。第二引数の Long は ID の型を表しています。
コントローラー
最後に MainController.kt を作成します。
@Controller
class MainController {
@Inject
lateinit var todoRepository: TodoRepository
}
Inject アノテーションをつけるとなんかいい感じになります。仕組みはわかっていません。
まずは GET メソッドに対する処理を実装します。データベースからすべてのデータを取得し、HTML をレンダリングします。次のようになります。
@Get("/index")
@View("index")
fun index(): HttpResponse<*> {
val dataList = todoRepository.findAll()
return HttpResponse.ok(mapOf("dataList" to dataList))
}
Get アノテーションは、/index に GET が飛んだときにこの関数を実行することを表します。View アノテーションにより、index.html をレンダリングすることができます。
findAll() を実行することで全件取得ができます。このように簡単な命令であれば SQL 文を書く必要はありません。
ここで得られたリストを index.html の中の dataList と紐づけます。ここでは Map を使っていますが、POJO でもできるようです。
次は追加の処理を実装します。
@Post("/add")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
fun add(@Body("text") text: String): HttpResponse<*> {
val data = TodoEntry(0, text)
todoRepository.save(data)
return HttpResponse.redirect<Any>(URI.create("/index"))
}
POST メソッドにより送られてきたリクエストのボディは Body アノテーションを使うことで受け取ることができます。データを保存し、/index にリダイレクトします。
同様に削除の処理も実装します。
@Post("/delete")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
fun delete(@Body("id") id: Long): HttpResponse<*> {
todoRepository.deleteById(id)
return HttpResponse.redirect<Any>(URI.create("/index"))
}
id の型を Long にしています。自動的に変換してくれますが、勿論数値に変換できない場合はエラーになります。本当はそのあたりの処理も書かないといけない気がしますが、今回は省略します。
完成したものがこちらになります。
package com.example
import io.micronaut.http.HttpResponse
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.*
import io.micronaut.views.View
import jakarta.inject.Inject
import java.net.URI
@Controller
class MainController {
@Inject
lateinit var todoRepository: TodoRepository
@Get("/index")
@View("index")
fun index(): HttpResponse<*> {
val dataList = todoRepository.findAll()
return HttpResponse.ok(mapOf("dataList" to dataList))
}
@Post("/add")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
fun add(@Body("text") text: String): HttpResponse<*> {
val data = TodoEntry(0, text)
todoRepository.save(data)
return HttpResponse.redirect<Any>(URI.create("/index"))
}
@Post("/delete")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
fun delete(@Body("id") id: Long): HttpResponse<*> {
todoRepository.deleteById(id)
return HttpResponse.redirect<Any>(URI.create("/index"))
}
}
実行
完成したので実行してみましょう。localhost:8080/index にアクセスします。
追加も削除も無事に動きました。
まとめ
Kotlin と Micronaut を使って簡単な Web アプリを作ってみました。
Micronaut の公式サイトを見るともっといろいろなことができるようなので、みなさんも挑戦してみてはいかがでしょうか。
参考文献
部分的に『実践Rustプログラミング入門』を参考にしました。Micronaut 公式ドキュメントなどを参考にしました。
追記
GitHub リポジトリを公開しました。
Discussion