🛰️

Kotlin と Micronaut で TODO アプリを作ってみた

2022/09/08に公開

はじめに

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 に対応しています。詳しくは公式サイトをご覧ください。

https://micronaut.io/

プロジェクトの作成

まずは IntelliJ IDEA を起動して、新規プロジェクトを作成します。Ultimate Edition であれば Micronaut を選べるので、それを選択します。そうでない場合、Micronaut Launch から同様のことができると思うのでそちらで行ってください。

名前を todo_micronaut (好きな名前で OK) にし、言語を Kotlin、ビルドシステムを Gradle にします。テストは今回使いません。残りはそのままにします。

次へを選択し、機能を追加します。

  • Micronaut Data JPA
  • H2 Database
  • Thymeleaf Views

を選択します。

作成をクリックし、しばらく待つと準備完了です。

テンプレートの作成

まず resources フォルダの中に views というフォルダを作り、その中に index.html というファイルを作成します。中身を次のようにします。

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 というファイルを作成します。

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 というファイルを作ります。

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 にしています。自動的に変換してくれますが、勿論数値に変換できない場合はエラーになります。本当はそのあたりの処理も書かないといけない気がしますが、今回は省略します。

完成したものがこちらになります。

MainController.kt
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 リポジトリを公開しました。

https://github.com/hako111223/todo-micronaut

Discussion