| この記事の要点 |
- Genie = Julia の Rails 風 MVC フルスタック Web フレームワーク
- 導入:
using Pkg; Pkg.add("Genie")。Genie.Generator.newcontroller("UserController") で雛形生成 - ルート → コントローラ関数 → ビューの流れ。
route("/users", UserController.index) - JSON レスポンスは
Genie.Renderer.Json.json(payload) - ビューは
.jl.html(HTML 内 Julia 式)/ .jl.md(Markdown)。Genie 5.x が現行
|
Genie Framework とは
Genie は Julia 製のフルスタック Web フレームワークで、Ruby on Rails や Django にインスパイアされた MVC アーキテクチャを持ちます。Julia の高速性を活かしてサイエンス系 / シミュレーション系 Web アプリ、ダッシュボード、API サーバに使われます。Genie Apps(フルスタック)、Stipple(リアクティブ UI)、SearchLight(ORM)などのサブパッケージで構成。
環境セットアップ
# Julia REPL(julia コマンドで起動)
using Pkg
Pkg.add("Genie")
# バージョン確認
using Genie
@info "Genie version" pkgversion(Genie)
新規アプリ作成
using Genie
Genie.Generator.newapp("MyApp")
# 生成される構成(抜粋):
# MyApp/
# ├── Project.toml
# ├── Manifest.toml
# ├── bootstrap.jl
# ├── config/
# │ ├── env/ ← 環境別設定 (dev / prod / test)
# │ ├── initializers/
# │ └── routes.jl ← ルート定義 ★
# ├── src/
# ├── app/
# │ ├── resources/
# │ │ └── home/ ← デフォルト resource
# │ └── layouts/
# └── public/
コントローラ生成
# プロジェクトディレクトリで Julia REPL を開いて
cd("MyApp")
using Genie
# Genie 5.x のジェネレータ
Genie.Generator.newcontroller("Users")
# 以下が作成される:
# app/resources/users/
# ├── UsersController.jl ← コントローラ
# ├── Users.jl ← モデル (任意)
# └── views/ ← ビュー
シンプルなコントローラを書く
# app/resources/users/UsersController.jl
module UsersController
using Genie.Renderer
using Genie.Renderer.Json
using Genie.Requests
# 一覧 (index)
function index()
users = [
Dict("id" => 1, "name" => "Alice"),
Dict("id" => 2, "name" => "Bob"),
]
return json(users)
end
# 詳細 (show)
function show()
id = params(:id)
user = Dict("id" => parse(Int, id), "name" => "Alice")
return json(user)
end
# 作成 (create)
function create()
payload = jsonpayload() # POST body の JSON
name = get(payload, "name", "")
return json(Dict("created" => true, "name" => name), status = 201)
end
# 更新 (update)
function update()
id = params(:id)
payload = jsonpayload()
return json(Dict("id" => id, "updated" => true, "data" => payload))
end
# 削除 (destroy)
function destroy()
id = params(:id)
return json(Dict("id" => id, "deleted" => true))
end
end # module
ルートの定義
# config/routes.jl
using Genie, Genie.Router
using UsersController
# 個別ルート
route("/users", UsersController.index, method = GET)
route("/users/:id", UsersController.show, method = GET)
route("/users", UsersController.create, method = POST)
route("/users/:id", UsersController.update, method = PUT)
route("/users/:id", UsersController.destroy, method = DELETE)
# 名前付きルート(URL ヘルパーで使う)
route("/users", UsersController.index, method = GET, named = :users_index)
# JSON API 専用接頭辞
route("/api/v1/users", UsersController.index, method = GET)
# HTML ビューを返すルート
route("/") do
html(:home, :index) # app/resources/home/views/index.jl.html を返す
end
サーバ起動
# Julia REPL で
using Genie
Genie.loadapp()
Genie.up()
# → http://127.0.0.1:8000
# シェルから直接
# julia --project=. -e "using Genie; Genie.loadapp(); up(async=false)"
# 環境を指定
# GENIE_ENV=prod julia --project=. -e "using Genie; ..."
# テスト
# curl http://127.0.0.1:8000/users
# [{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]
パラメータの受け取り
| 取得元 | 取得方法 | 例 |
| URL パラメータ | params(:id) | /users/:id → id 取得 |
| クエリ文字列 | params(:page) | ?page=2 → "2" |
| POST フォーム | postpayload() | Dict として全フィールド |
| JSON ボディ | jsonpayload() | Dict にパース済 |
| ヘッダ | getheaders() | Dict |
| Cookie | getcookies() | Dict |
using Genie.Requests
function search()
# ?q=julia&page=2
q = params(:q, "") # 第 2 引数はデフォルト値
page = parse(Int, params(:page, "1"))
limit = parse(Int, params(:limit, "20"))
return json(Dict(
"query" => q,
"page" => page,
"limit" => limit,
))
end
function login()
body = jsonpayload()
email = get(body, "email", nothing)
password = get(body, "password", nothing)
if email === nothing || password === nothing
return json(Dict("error" => "Missing fields"), status = 400)
end
return json(Dict("token" => "fake-jwt-token"))
end
ビューを返す(HTML レスポンス)
HTML テンプレート (.jl.html)
<!-- app/resources/users/views/index.jl.html -->
<h1>Users</h1>
<ul>
<% for u in users %>
<li><%= u["name"] %> (id: <%= u["id"] %>)</li>
<% end %>
</ul>
<p>Total: <%= length(users) %></p>
コントローラからビューを呼ぶ
module UsersController
using Genie.Renderer, Genie.Renderer.Html
using Genie.Requests
function index()
users = [
Dict("id" => 1, "name" => "Alice"),
Dict("id" => 2, "name" => "Bob"),
]
return html(:users, :index, users = users)
# app/resources/users/views/index.jl.html に users を渡す
end
end
Markdown ビュー (.jl.md)
<!-- app/resources/about/views/index.jl.md -->
# About
This site runs on **Genie Framework** <%= Genie.Configuration.GENIE_VERSION %>.
- Julia version: <%= VERSION %>
- Environment: <%= Genie.Configuration.config.app_env %>
レスポンスの種類
| 関数 | 用途 | モジュール |
json(payload) | JSON レスポンス | Genie.Renderer.Json |
html(:resource, :view, vars...) | HTML テンプレートを描画 | Genie.Renderer.Html |
respond(string, status) | 素の文字列 | Genie.Renderer |
redirect("/path") | リダイレクト | Genie.Renderer |
sendfile("path") | ファイル送信 | Genie.Renderer |
ステータスコードとヘッダ
function not_found()
return json(Dict("error" => "User not found"), status = 404)
end
function with_custom_header()
return json(
Dict("ok" => true),
status = 200,
headers = Dict(
"X-Request-Id" => "abc123",
"Cache-Control" => "no-cache",
),
)
end
function created()
return json(Dict("id" => 42), status = 201, headers = Dict(
"Location" => "/users/42",
))
end
例外処理
using Genie.Exceptions
function show()
id = params(:id)
user = find_user(id)
if user === nothing
throw(ExceptionalResponse(json(
Dict("error" => "Not found"),
status = 404,
)))
end
return json(user)
end
ミドルウェア相当(before / after フック)
# config/initializers/middleware.jl などで
using Genie.Router
# 全リクエストの前にログ出力
Genie.Router.before_request() do
@info "Request" method = params(:method) path = params(:path)
end
# CORS のためのレスポンス改変はミドルウェアで
# config/initializers/cors.jl
push!(Genie.Router.pre_match_hooks, function (params)
# ...
end)
MVC 全体像(おさらい)
| レイヤ | 役割 | 配置 |
| Model | データ定義、DB アクセス(SearchLight ORM) | app/resources/<name>/<Name>.jl |
| View | 表示テンプレート | app/resources/<name>/views/*.jl.html |
| Controller | リクエスト処理 | app/resources/<name>/<Name>Controller.jl |
| Router | URL → Controller 関数のマッピング | config/routes.jl |
テスト
# test/test_users.jl
using Test
using HTTP
using Genie
@testset "UsersController" begin
response = HTTP.get("http://127.0.0.1:8000/users")
@test response.status == 200
payload = HTTP.JSONService.json(String(response.body))
@test length(payload) == 2
@test payload[1]["name"] == "Alice"
end
トラブルシューティング
| 症状 | 原因 | 対処 |
| 404 Not Found なのにルートはある | HTTP メソッド指定の不一致 | method = POST 等を確認 |
| UsersController が見つからない | routes.jl で using 漏れ | using UsersController 追加 |
| params(:id) が nothing | ルートが /users で :id プレースホルダ無し | /users/:id に修正 |
| JSON が文字列で返る | respond() ではなく json() を使う | Genie.Renderer.Json 経由 |
| 変更が反映されない | 開発時はコード変更後再起動 or Revise.jl 導入 | using Revise; Genie.loadapp() |
FAQ
Q: 自動リロードしたい
A: Revise.jl を使うと Julia コードの変更を REPL から検知して反映できます。Genie の REPL 開発フローと相性が良いです。
Q: Genie と Stipple の関係は?
A: Genie は基盤 Web フレームワーク、Stipple は Genie の上に乗るリアクティブ UI ライブラリ(Vue 系)。サイエンス系ダッシュボードは Stipple + GenieFramework がよく使われます。
Q: Django や Rails より速い?
A: Julia ネイティブの速度を活かせるので CPU バウンドな処理(数値計算、画像処理)では速い。I/O バウンドな単純 API では同等。エコシステムは Django/Rails が圧倒的に充実。