DEV Community

Cover image for How To Integrate HoneySQL with PostgreSQL

How To Integrate HoneySQL with PostgreSQL

kelvinmai profile image Kelvin Mai ・3 min read

In returning to clojure, there was one library that I wanted to check out and that one is honeysql. HoneySQL is a library where you can create a clojure map and use the sql/format function to turn that map into a raw sql query.

(require '[honeysql.core :as sql]
         '[honeysql.helpers :refer :all :as helpers])

(def sqlmap {:select [:a :b :c]
             :from   [:foo]
             :where  [:= :f.a "baz"]})

(sql/format sqlmap)
;; => ["SELECT a, b, c FROM foo WHERE f.a = ?" "baz"]
Enter fullscreen mode Exit fullscreen mode

The above example is one that shows off the capabilities of the library, but below we have the classic "get everything" query for simplicity's sake.

(def sqlmap {:select [:*]
             :from   [:bar]})

(sql/format sqlmap)
;; => ["SELECT * FROM bar"]
Enter fullscreen mode Exit fullscreen mode

But there is one small problem, the library is very useful in creating raw query strings, but I couldn't find an example of actually executing these queries, and that is what I want to accomplish with this post. For this post we'll be using the Clojure CLI rather than lieningen, so I have included the minimum configuration of a deps.edn file below as well as a nrepl alias.

{:paths ["src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}
        org.postgresql/postgresql {:mvn/version "42.2.2"}
        seancorfield/next.jdbc {:mvn/version "1.1.613"}
        honeysql/honeysql {:mvn/version "1.0.444"}}
 :aliases {:nrepl {:extra-deps {nrepl/nrepl {:mvn/version "0.8.0"}
                                cider/cider-nrepl {:mvn/version "0.25.2"}}
                   :main-opts ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]"]}}
Enter fullscreen mode Exit fullscreen mode

Some of these versions may have been updated, so make sure to check clojars, and for this example I will be using postgres as the database of choice if you are instead interested in a different data source, relevant links to the drivers are available on the clojure jdbc library readme. Speaking of which, even though we'll be using next.jdbc, the initial configuration def will be the same as shown below.

(ns example.db
  (:require [next.jdbc :as jdbc]))

(def db-config
  {:dbtype "postgresql"
   :dbname "example_db"
   :host "localhost"
   :user "postgres"
   :password "postgres"})

(def db (jdbc/get-datasource db-config))
Enter fullscreen mode Exit fullscreen mode

I'm unsure what the difference between the original jdbc and next.jdbc is, but figured it would be a good idea to use the latest and greatest connection library and skip whatever headaches were present that prompted the creation of this new library. Here you can see that connection configuration being used by jdbc/get-datasource and the persisted connection is held in the db var. And now all we need to do now is to execute queries which we can do by calling jdbc/execute!. It's important to note that the first argument is the database connection which we've defined above.

(jdbc/execute! db ["CREATE TABLE thing (
                     id SERIAL,
                     title TEXT,
                     description TEXT)"])
;; => [#:next.jdbc{:update-count 0}]

(jdbc/execute! db ["SELECT * FROM thing"])
;; => []
Enter fullscreen mode Exit fullscreen mode

And lastly instead of writing raw sql, we come full circle and can now use the honeysql library to pass in formatted sql maps. So simply replace the vector of sql queries with some honeysql operations. So if we were to run the following in the repl, we'll get some desired side effects. (The libraries are aliased as above from the require statements.)

(jdbc/execute! db (sql/format
                      {:select [:*]
                       :from [:thing]}))
;; => []

(jdbc/execute! db (sql/format
                      {:insert-into :thing
                       :columns [:text :description]
                       :values [["blog" "written articles"]
                                ["article" "blog post"]]}))
;; => [#:next.jdbc{:update-count 2}]
;; => [#:thing{:id 1, :title "blog", :description "written articles"}

(jdbc/execute! db (sql/format
                      {:select [:*]
                       :from [:thing]}))
;; => [#:thing{:id 2, :title "article", :description "blog post"}]
Enter fullscreen mode Exit fullscreen mode

And that's all there is to it, I hope some have found this post useful.


Follow and support me


Editor guide