前回はClojure
Ring
でjsonを扱えるようになりました。
これで全ての準備が整ったので、本格的にTodoアプリを作っていきます
構成
ファイル | 役割 |
---|---|
core.clj |
ルーティングやミドルウェアの注入、main関数 |
handler.clj |
各ルーティングのハンドラー |
driver.clj |
Dababaseの処理( jdbc ) |
core.clj
core.clj
(ns todo.core
(:require [ring.adapter.jetty :as jetty]
[ring.util.response :as res]
[ring.middleware.json :refer [wrap-json-response wrap-json-body]]
[compojure.core :refer [defroutes GET POST PUT DELETE]]
[compojure.route :as route]
[todo.handler :as handler]))
(defroutes handler'
(POST "/v1/todos" req handler/post-todos)
(GET "/v1/todos" [] handler/get-todos)
(GET "/v1/todos/:id" [id] (handler/get-todo id))
(PUT "/v1/todos/:id" [id :as req] (handler/put-todo req id))
(DELETE "/v1/todos/:id" [id] (handler/delete-todo id))
(route/not-found (res/response {:message "Not Found"})))
(def handler
(-> handler'
(wrap-json-body {:keywords? true})
(wrap-json-response)))
(defn -main []
(jetty/run-jetty handler {:port 3000}))
ここではルーティングとミドルウェアの注入をしています
ルーティング
compojure
を使い、シンプルにルーティングを実現しています
(defroutes handler'
(POST "/v1/todos" req handler/post-todos)
(GET "/v1/todos" [] handler/get-todos)
(GET "/v1/todos/:id" [id] (handler/get-todo id))
(PUT "/v1/todos/:id" [id :as req] (handler/put-todo req id))
(DELETE "/v1/todos/:id" [id] (handler/delete-todo id))
(route/not-found (res/response {:message "Not Found"})))
ミドルウェア
json
を扱うために ring-json
のミドルウェアを注入しています
(def handler
(-> handler'
(wrap-json-body {:keywords? true})
(wrap-json-response)))
handler.clj
ここでは各リクエストを処理するハンドラーを設定しています
handler.clj
(ns todo.handler
(:require [ring.util.response :as res]
[todo.driver :as driver]))
(defn get-todo [id]
(-> (driver/find-todo id)
(res/response)))
(defn get-todos [_]
(-> (driver/get-todos)
(res/response)))
(defn post-todos [req]
(let [{:keys [title completed]} (get-in req [:body])]
(-> (driver/insert-todo title completed)
(res/response))))
(defn put-todo [req id]
(let [{:keys [title completed]} (get-in req [:body])]
(-> (driver/update-todo id title completed)
(res/response))))
(defn delete-todo [id]
(driver/delete-todo id)
(res/response {:message "Deleted"}))
driver.clj
ここではjdbc
を利用してDBと接続し、Todoのデータ取得や更新をしています
driver.clj
(ns todo.driver
(:require [next.jdbc :as jdbc]
[next.jdbc.result-set :as rs]))
(def db-spec
{:dbtype "postgresql"
:dbname "todo"
:host "localhost"
:port 5432
:user "todo-user"
:password "todo-password"})
(def db (jdbc/get-datasource db-spec))
(defn get-todos
[]
(jdbc/execute! db ["SELECT * FROM todos"]
{:builder-fn rs/as-unqualified-lower-maps}))
(defn find-todo
[id]
(-> (jdbc/execute! db
["SELECT * FROM todos WHERE id = ?" (Integer/parseInt id)]
{:builder-fn rs/as-unqualified-lower-maps})
(first)))
(defn insert-todo
[title completed]
(-> (jdbc/execute! db
["INSERT INTO todos (title, completed) VALUES (?, ?) RETURNING id, title, completed" title completed]
{:builder-fn rs/as-unqualified-lower-maps})
(first)))
(defn update-todo
[id title completed]
(-> (jdbc/execute! db
["UPDATE todos SET title = ?, completed = ? WHERE id = ? RETURNING id, title, completed" title completed (Integer/parseInt id)]
{:builder-fn rs/as-unqualified-lower-maps})
(first)))
(defn delete-todo
[id]
(jdbc/execute! db ["DELETE FROM todos WHERE id = ?" (Integer/parseInt id)]))
リクエストを送ってみる
GET /v1/todos
$ curl localhost:3000/v1/todos
[
{
"id": 1,
"title": "朝食を食べる",
"completed": false
}
]
GET /v1/todos/1
$ curl localhost:3000/v1/todos/1
{
"id": 1,
"title": "朝食を食べる",
"completed": false
}
POST /v1/todos
$ curl -X POST -d '{"title": "追加Todo", "completed": false}' -H 'Content-Type: application/json' localhost:3000/v1/todos
{
"id": 2,
"title": "追加Todo",
"completed": false
}
PUT /v1/todos/:id
$ curl -X PUT -d '{"title": "更新Todo", "completed": true}' -H 'Content-Type: application/json' localhost:3000/v1/todos/2
{
"id": 2,
"title": "更新Todo",
"completed": true
}
DELETE /v1/todos/:id
$ curl -X DELETE localhost:3000/v1/todos/2
{
"message": "Deleted"
}
完成しました!
明日からはこのTodoアプリをベースにloggingを追加したりしてみます