はじめに
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 というパッケージを使います.
ここからは、実際に僕が書いたコードをもとに説明を進めます.
以下がそのコードの全体です.
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 との連携に関するコードです.
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)
で実行できます.
ルーティング
次はルーティングを定義している部分です.
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
等がありますが,使い方は全部同じで
Get("path", (request, HTTP) -> begin
# hogepiyo
end)
のようにして使えます.
マクロによる定義は以下のようになります.
@route GET "path" begin
# hogepiyo
end
マクロによる定義の方がすっきりしていて良さそうに見えますが,外部で定義された変数や関数を使う際に手間がかかります.
例えば以下の関数とマクロは同じものを定義しています.
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をいくつか追加してみると,
このようになります.
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!を押すと,
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 で書く,というのもありなんじゃないかと思いました.