Persist items to the database
The database that was chosen here is SQLite, because it's dead simple to setup and more than enough for this project. Please note that I took some liberty with the assignment. I chose to use a numeric field for the `id` column of an item. This leverages automatic creation and incrementation of the id by SQLite itself.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ pom.xml.asc
|
||||
/.lsp
|
||||
/.nrepl-port
|
||||
/.prepl-port
|
||||
*.db
|
||||
|
||||
@@ -20,6 +20,7 @@ default, the server listens on port 3000.
|
||||
* `reitit`: for handling routes
|
||||
* `jetty`: web server
|
||||
* `muuntaja`: JSON handling
|
||||
* `next.jdbc`: database interface (SQLite)
|
||||
|
||||
## Documentation links
|
||||
|
||||
@@ -29,3 +30,5 @@ The following links proved more than useful when working on this assignment:
|
||||
* <https://github.com/metosin/reitit/blob/master/doc/ring/content_negotiation.md>
|
||||
* <https://ostash.dev/posts/2021-08-22-data-validation-in-clojure/>
|
||||
* <https://clojurecivitas.github.io/malli/elements_of_malli.html>
|
||||
* <https://github.com/metosin/reitit/blob/master/doc/coercion/malli_coercion.md>
|
||||
* <https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.3.1086/doc/getting-started>
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
[ring/ring-jetty-adapter "1.15.3"]
|
||||
[metosin/reitit "0.10.0"]
|
||||
[metosin/reitit-malli "0.10.0"]
|
||||
[metosin/muuntaja "0.6.11"]]
|
||||
[metosin/muuntaja "0.6.11"]
|
||||
[com.github.seancorfield/next.jdbc "1.3.1086"]
|
||||
[org.xerial/sqlite-jdbc "3.51.1.0"]]
|
||||
:main ^:skip-aot yohoho.core
|
||||
:target-path "target/%s"
|
||||
:profiles {:uberjar {:aot :all
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
[reitit.ring.middleware.muuntaja :as muuntaja]
|
||||
[muuntaja.core :as m]
|
||||
[reitit.coercion.malli :as malli]
|
||||
[reitit.ring.coercion :as coercion])
|
||||
[reitit.ring.coercion :as coercion]
|
||||
[next.jdbc :as jdbc]
|
||||
[next.jdbc.sql :as sql]
|
||||
[next.jdbc.result-set :as rs])
|
||||
(:gen-class))
|
||||
|
||||
|
||||
@@ -23,6 +26,38 @@
|
||||
[:email Email]])
|
||||
|
||||
|
||||
;; Database stuff -------------------------------------------------
|
||||
(def db {:dbtype "sqlite" :dbname "yohoho.db"})
|
||||
|
||||
(defn init-db!
|
||||
"Create database structure if needed"
|
||||
[]
|
||||
(jdbc/execute!
|
||||
db
|
||||
["CREATE TABLE IF NOT EXISTS items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT NOT NULL)"]))
|
||||
|
||||
(defn db-store-item!
|
||||
"Store a new item in database and returns it ID"
|
||||
[name email]
|
||||
(let [db-result (sql/insert! db :items {:name name, :email email})]
|
||||
;; SQLite returns the generated ID of the new item like this:
|
||||
;; {:last_insert_rowid() 42}
|
||||
(get db-result (keyword "last_insert_rowid()"))))
|
||||
|
||||
(defn db-get-item
|
||||
"Retrieve an item given its ID"
|
||||
[id]
|
||||
(sql/get-by-id db :items id
|
||||
{:builder-fn rs/as-unqualified-lower-maps}))
|
||||
|
||||
(defn db-get-all-items
|
||||
"Retrieve every item from the database"
|
||||
[]
|
||||
(sql/query db ["select * from items"] {:builder-fn rs/as-unqualified-lower-maps}))
|
||||
|
||||
;; Handlers -------------------------------------------------------
|
||||
(defn ahoy-handler
|
||||
[_]
|
||||
@@ -32,19 +67,18 @@
|
||||
(defn get-items-handler
|
||||
[_]
|
||||
{:status 200
|
||||
:body {:id 1
|
||||
:name "Jack Sparrow"
|
||||
:email "jack.sparrow@triangle.bm"}})
|
||||
:body {:items (db-get-all-items)}})
|
||||
|
||||
(defn post-items-handler
|
||||
(defn create-item-handler
|
||||
[request]
|
||||
(let [item (:body-params request)
|
||||
id 1
|
||||
name (:name item)
|
||||
email (:email item)]
|
||||
email (:email item)
|
||||
id (db-store-item! name email)]
|
||||
|
||||
{:status 201
|
||||
:headers {"Location" (str "/item/" id)}
|
||||
:body {:id id, :name name, :email email}}))
|
||||
:body (db-get-item id)}))
|
||||
|
||||
|
||||
;; Custom middleware ----------------------------------------------
|
||||
@@ -68,7 +102,7 @@
|
||||
(ring/router
|
||||
[["/ahoy" {:get ahoy-handler}]
|
||||
["/items" {:get get-items-handler
|
||||
:post {:handler post-items-handler
|
||||
:post {:handler create-item-handler
|
||||
:parameters {:body Item}}}]]
|
||||
;; Middlewares:
|
||||
;; - wrap-content-type-json: ensure POST routes are sent JSON payload
|
||||
@@ -92,5 +126,8 @@
|
||||
"HolidayPirates take-home assignement.
|
||||
Goal: build a (very) small REST API that exposes to endpoints."
|
||||
[& args]
|
||||
(println "Initializing SQLite database...")
|
||||
(init-db!)
|
||||
(println "Starting server...")
|
||||
(http-server/run-jetty app {:port 3000 :join? false})
|
||||
(println "Ahoy! Yo-ho-ho API running on port 3000"))
|
||||
|
||||
Reference in New Issue
Block a user