Julia と React でTODOアプリを作る

# はじめに
julia と React を使ってTODOアプリを作ります．
julia の Mearly.jl というパッケージを使ってみたかったという動機があったので，React に関しては軽く流します．

ちなみに，web ど素人です．

コードは github で公開しています ( https://github.com/kogad/JuliaReactToDo ).

# バックエンド
まず Merly.jl (https://github.com/neomatrixcode/Merly.jl) について説明します．
READMEによれば，

> Merly is a micro framework for declaring routes and handling requests. Quickly creating web applications in Julia with minimal effort.

だそうです．
僕は web ど素人なので micro framework が具体的にどういうものを指しているかはよくわからないんですが，大雑把には web のフレームワークでルーティングとかがお手軽にできるって理解でよさそうです．

次にDBですが，僕は MySQL しか触ったことがないので MySQL を使います．
julia から MySQL を触るために MySQL.jl というパッケージを使います．

ここからは、実際に僕が書いたコードをもとに説明を進めます．
以下がそのコードの全体です．

```julia
using Merly
using JSON
using MySQL
using Tables: rowtable


const host = "127.0.0.1"
const user = "root"
const password = ""

const conn = DBInterface.connect(MySQL.Connection, host, user, password)
DBInterface.execute(conn, "create database if not exists julia_react_todo")

create_table = """
create table if not exists julia_react_todo.todos(
    id int PRIMARY KEY,
    description varchar(255),
    deadline date
)
"""
DBInterface.execute(conn, create_table)

function find_all(conn)
    res = DBInterface.execute(conn, "select * from julia_react_todo.todos")
    return json(rowtable(res))
end

function delete(conn, id)
    DBInterface.execute(conn, "delete from julia_react_todo.todos where id = $id")
end

function insert(conn, todo)
    id = todo["id"]
    description = todo["description"]
    deadline = todo["deadline"]
    DBInterface.execute(conn, "insert into julia_react_todo.todos values($id, \"$description\", \"$deadline\")")
end

useCORS(
AllowOrigins = "*"
, AllowHeaders = "Origin, Content-Type, Accept"
, AllowMethods = "GET,POST,PUT,DELETE"
, MaxAge = "178000")

Get("/todo", (request, HTTP) -> begin
    println(find_all(conn))

    HTTP.Response(201
          , HTTP.mkheaders(["Content-Type" => "application/json"])
          ,body=find_all(conn))
end)

Post("/todo", (request, HTTP) -> begin
    println(request.body)
    todo = JSON.parse(request.body)
    insert(conn, todo)
    HTTP.Response(201, "ok")
end)

Delete("/todo/:id", (request, HTTP) -> begin
    delete(conn, request.params["id"])
    HTTP.Response(201, "ok")
end)


start(host = "127.0.0.1", port = 8086, verbose = true)
```

## MySQL との連携

まずは MySQL との連携について説明します．
上記のコード中の以下の部分が，MySQL.jl を用いた MySQL との連携に関するコードです．

```julia
const host = "127.0.0.1"
const user = "root"
const password = ""

const conn = DBInterface.connect(MySQL.Connection, host, user, password)
DBInterface.execute(conn, "create database if not exists julia_react_todo")

create_table = """
create table if not exists julia_react_todo.todos(
    id int PRIMARY KEY,
    description varchar(255),
    deadline date
)
"""
DBInterface.execute(conn, create_table)

function find_all(conn)
    res = DBInterface.execute(conn, "select * from julia_react_todo.todos")
    return json(rowtable(res))
end

function delete(conn, id)
    DBInterface.execute(conn, "delete from julia_react_todo.todos where id = $id")
end

function insert(conn, todo)
    id = todo["id"]
    description = todo["description"]
    deadline = todo["deadline"]
    DBInterface.execute(conn, "insert into julia_react_todo.todos values($id, \"$description\", \"$deadline\")")
end
```

まず DB との接続は`conn = DBInterface.connect(MySQL.Connection, host, user, password)`で行います．`host` 等は適宜変更してください．
`DBInterface`は MySQL.jl が依存している DBInterface.jl のことで，DBInterface.jl は一般的なDB操作の julia でのインターフェースを定義しています．
SQL 文は `DBInterface.execute(conn, sql)` で実行できます．


## ルーティング
次はルーティングを定義している部分です．

```julia
Get("/todo", (request, HTTP) -> begin
    println(find_all(conn))

    HTTP.Response(201
          , HTTP.mkheaders(["Content-Type" => "application/json"])
          ,body=find_all(conn))
end)

Post("/todo", (request, HTTP) -> begin
    println(request.body)
    todo = JSON.parse(request.body)
    insert(conn, todo)
    HTTP.Response(201, "ok")
end)

Delete("/todo/:id", (request, HTTP) -> begin
    delete(conn, request.params["id"])
    HTTP.Response(201, "ok")
end)
```

Merly.jl では，ルーティングを関数またはマクロを使って定義できます．
ここでは関数を使って定義しています．
`Get` の他にも `Post`,`Put`, `Delete` 等がありますが，使い方は全部同じで

```julia
Get("path", (request, HTTP) -> begin
    # hogepiyo
end)
```
のようにして使えます．

マクロによる定義は以下のようになります．

```julia
@route GET "path" begin
    # hogepiyo
end
```

マクロによる定義の方がすっきりしていて良さそうに見えますが，外部で定義された変数や関数を使う際に手間がかかります．
例えば以下の関数とマクロは同じものを定義しています．

```julia
Post("/todo", (request, HTTP) -> begin
    println(request.body)
    todo = JSON.parse(request.body)
    insert(conn, todo)
    HTTP.Response(201, "ok")
end)

@route POST "/todo" (;JSON=JSON, insert=insert) begin
    println(request.body)
    todo = JSON.parse(request.body)
    insert(conn, todo)
    HTTP.Response(201, "ok")
end
```

どちらも1行目以外は全く同じなので，どちらを使うかは好みの問題かなあと思います．


# フロントエンド（React）
ここでは，以前僕が作ったものを流用します．
React でTODOアプリを作る記事はたくさんあるので，そちらを参照してください．
たしか以下のあたりを参考にして作りました．

- https://qiita.com/rioc/items/8723c236e10d989e827d
- https://qiita.com/tatsurou313/items/8ebccf349e37f6185bb6

バックエンドに Julia を使うからと言って，フロントエンド側で何か特別なことをする必要はありません．
上で定義したルーティングの通り，
`/todo` に`GET`, `POST`, `DELETE` を投げられればOKです．

コードはgithubに公開している通りです（https://github.com/kogad/JuliaReactToDo/tree/main/client ）．

# 動かしてみる

まず MySQL を先に動かしておきます．
バックエンドは `src/JuliaToDoApp.jl` を実行すれば良いです．
フロントエンドは，`client` に入って `npm start` で動きます．
`localhost:3000`にアクセスしましょう．
各種パッケージ等のインストールは適宜行ってください．

まだTODOを追加していないので，TODOを追加するフォームだけが表示されていると思います．

TODOをいくつか追加してみると，

![スクリーンショット 2021-04-20 11.19.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563409/2f92482a-db0e-f8e4-5dc6-3ce2800efb63.png)

このようになります．
DBも確認すると，

```
mysql> select * from julia_react_todo.todos;
+----+--------------------+------------+
| id | description        | deadline   |
+----+--------------------+------------+
|  1 | 何かする           | 2021-04-20 |
|  2 | 何かする！         | 2021-04-20 |
|  3 | 何かする！！       | 2021-04-24 |
+----+--------------------+------------+
3 rows in set (0.00 sec)

```

となっていて，ちゃんとTODOが追加されています．

2番目のTODOを削除してみます．
チェックを入れてDone!を押すと，

![スクリーンショット 2021-04-20 11.23.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563409/6cb07f7b-3597-9b98-5f8f-866edd97067a.png)


```
mysql> select * from julia_react_todo.todos;
+----+--------------------+------------+
| id | description        | deadline   |
+----+--------------------+------------+
|  1 | 何かする           | 2021-04-20 |
|  3 | 何かする！！       | 2021-04-24 |
+----+--------------------+------------+
2 rows in set (0.00 sec)

```

消えました．

# おわりに（感想）
Merly.jl はお手軽に使えてよいなあと思いました．
julia の web フレームワークといえば Genie.jl がありますが，ちょっとしたものを作るならフロントエンドは React で書いてバックエンドは Merly.jl で書く，というのもありなんじゃないかと思いました．