0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【ひとりカレンダー】ClojureAdvent Calendar 2024

Day 16

Clojure: RingでTodoアプリを作る - ベース完成

Last updated at Posted at 2024-12-15

advent_calendar_2024.png

Advent Calendar 2024 Day 16

前回は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を追加したりしてみます

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?