Skip to content

Commit eb9fc2a

Browse files
committed
Add 'SQL Database' section
1 parent 0ee3739 commit eb9fc2a

File tree

1 file changed

+142
-4
lines changed

1 file changed

+142
-4
lines changed

index.adoc

Lines changed: 142 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ content:
6565
[,clojure]
6666
----
6767
{:deps {org.clojure/clojure {:mvn/version "1.12.0"}
68-
org.duct-framework/main {:mvn/version "0.1.3"}}
68+
org.duct-framework/main {:mvn/version "0.1.4"}}
6969
:aliases {:duct {:main-opts ["-m" "duct.main"]}}}
7070
----
7171

@@ -241,7 +241,7 @@ We'll first add the new dependency:
241241
[,clojure]
242242
----
243243
{:deps {org.clojure/clojure {:mvn/version "1.12.0"}
244-
org.duct-framework/main {:mvn/version "0.1.3"}
244+
org.duct-framework/main {:mvn/version "0.1.4"}
245245
org.duct-framework/module.logging {:mvn/version "0.6.5"}}
246246
:aliases {:duct {:main-opts ["-m" "duct.main"]}}}
247247
----
@@ -556,7 +556,7 @@ modules: logging and web.
556556
[,clojure]
557557
----
558558
{:deps {org.clojure/clojure {:mvn/version "1.12.0"}
559-
org.duct-framework/main {:mvn/version "0.1.3"}
559+
org.duct-framework/main {:mvn/version "0.1.4"}
560560
org.duct-framework/module.logging {:mvn/version "0.6.5"}
561561
org.duct-framework/module.web {:mvn/version "0.11.1"}}
562562
:aliases {:duct {:main-opts ["-m" "duct.main"]}}}
@@ -772,8 +772,146 @@ Or we could return the string directly:
772772
<html lang=\"en\">
773773
<head><title>Hello World Wide Web</title></head>
774774
<body><h1>Hello World Wide Web</h1></body>
775-
</html>
775+
</html>"}))
776776
----
777777

778778
All of these examples are equivalent, but returning a vector is the most
779779
convenient and concise.
780+
781+
=== SQL Database
782+
783+
The next step is to add a database to our application. We'll use
784+
https://www.sqlite.org/index.html[SQLite], which means we need the
785+
corresponding JDBC adapter as a dependency.
786+
787+
To give us a Clojure-friendly way of querying the database, we'll also
788+
add a dependency on
789+
https://github.com/seancorfield/next-jdbc[next.jdbc].
790+
791+
Finally, we'll add the Duct SQL module. This will add a connection pool
792+
to the system that we can use to access the database.
793+
794+
Our project dependencies should now look like this:
795+
796+
.deps.edn
797+
[,clojure]
798+
----
799+
{:deps {org.clojure/clojure {:mvn/version "1.12.0"}
800+
org.duct-framework/main {:mvn/version "0.1.4"}
801+
org.duct-framework/module.logging {:mvn/version "0.6.5"}
802+
org.duct-framework/module.web {:mvn/version "0.11.1"}
803+
org.duct-framework/module.sql {:mvn/version "0.7.1"}
804+
org.xerial/sqlite-jdbc {:mvn/version "3.47.0.0"}
805+
com.github.seancorfield/next.jdbc {:mvn/version "1.3.955"}}
806+
:aliases {:duct {:main-opts ["-m" "duct.main"]}}}
807+
----
808+
809+
We can load these new dependencies either by restarting the REPL, or by
810+
using the `sync-deps` function.
811+
812+
[,clojure]
813+
----
814+
user=> (sync-deps)
815+
[...]
816+
----
817+
818+
The next step is to add `:duct.module/sql` to our Duct configuration.
819+
820+
.duct.edn
821+
[,clojure]
822+
----
823+
{:system
824+
{:duct.module/logging {}
825+
:duct.module/sql {}
826+
:duct.module/web
827+
{:features #{:site}
828+
:routes [["/" {:get :todo.routes/index}]]}}}
829+
----
830+
831+
Then reset via the REPL:
832+
833+
[,shell]
834+
----
835+
user=> (reset)
836+
:reloading ()
837+
Execution error (ExceptionInfo) at integrant.core/unbound-vars-exception (core.cljc:343).
838+
Unbound vars: jdbc-url
839+
----
840+
841+
Wait, what's this about an unbound var? Where did that come from?
842+
843+
Modules can add vars, and the SQL module adds one called `jdbc-url`.
844+
This var can be set via:
845+
846+
- A command-line argument, `--jdbc-url`
847+
- An environment variable, `JDBC_DATABASE_URL`
848+
849+
We can also set a default value for this var via the configuration. As
850+
SQLite uses a local file for its database, we can add a default to be
851+
used in development.
852+
853+
.duct.edn
854+
[,clojure]
855+
----
856+
{:vars {jdbc-url {:default "jdbc:sqlite:todo.db"}}
857+
:system
858+
{:duct.module/logging {}
859+
:duct.module/sql {}
860+
:duct.module/web
861+
{:features #{:site}
862+
:routes [["/" {:get :todo.routes/index}]]}}}
863+
----
864+
865+
If we want to change this in production, we can use the corresponding
866+
command-line argument or environment variable to override this default.
867+
868+
[,shell]
869+
----
870+
user=> (reset)
871+
:reloading ()
872+
:resumed
873+
----
874+
875+
The SQL module adds a database connection, but we need to add a ref to
876+
our route handlers in order for them to use it. We could do this
877+
by adding a ref to each handler:
878+
879+
[,clojure]
880+
----
881+
{:todo.routes/index {:db #ig/ref :duct.database/sql}
882+
----
883+
884+
This is useful if only some routes need to access the database. However,
885+
in this case, we expect that all routes will need database access in
886+
some fashion. To make this easier, the web module has an option,
887+
`:handler-opts` that applies common options to all route handlers it
888+
generates.
889+
890+
.duct.edn
891+
[,clojure]
892+
----
893+
{:vars {jdbc-url {:default "jdbc:sqlite:todo.db"}}
894+
:system
895+
{:duct.module/logging {}
896+
:duct.module/sql {}
897+
:duct.module/web
898+
{:features #{:site}
899+
:handler-opts {:db #ig/ref :duct.database/sql}
900+
:routes [["/" {:get :todo.routes/index}]]}}}
901+
----
902+
903+
This will add a `javax.sql.DataSource` object to the `:db` key of the
904+
component options. We can access this from the route handler function we
905+
created earlier.
906+
907+
.src/todo/routes.clj
908+
[,clojure]
909+
----
910+
(ns todo.routes)
911+
912+
(defn index [{:keys [db]}]
913+
(fn [_request]
914+
[:html {:lang "en"}
915+
[:head [:title "Hello World Wide Web"]]
916+
[:body [:h1 "Hello World Wide Web"]]]))
917+
----

0 commit comments

Comments
 (0)