Installation
Project dependencies
[walkable "1.3.0-alpha0"]
Walkable can work independently or as a resolver for Pathom. The Pathom approach is recommended because it’s easy to integrate seamlessly with other systems.
Don’t worry if you don’t know how Pathom works yet: Understanding Pathom is not required unless you use advanced features. |
Elements of a Walkable system
We talked about the high-level of Walkable with its three elements in Overview. (If you don’t know what they are, please go back and read!)
Now let’s see each of them in action with an unimpressive example.
Your first EQL query
Assume you have a table person
. Now you want to fetch the columns
name
and yob
.
The SQL way:
The EQL way:
[{:people/list [:person/name :person/yob]}]
;; We don't have runable code yet.
;; This is what I promise Walkable can return
;; if you follow till the end of this page!
{:people/list
[{:person/name "joe", :person/yob 1990}
{:person/name "eoj", :person/yob 2000}]}
Let’s examine the above query a bit. :people/list
is just an
arbitrary keyword to which we decide to assign the table person
.
EQL results, by design, have a tree-like structure. EQL queries,
similarly, reflect a tree-like semantic. In our example query above,
the two keywords :person/name
and :person/yob
are "children" of
the :people/list
branch.
:people/list
, being at the top level of the query, is called a root.
TODO: explain what an "entity" is here.
The registry
In the Overview, we learned that the registry is
the most important part of building the execute-query
function.
Let’s make a "just enough" version so we can execute the EQL query above:
(def registry
[{:key :people/list
:type :root (1)
:table "person"
:output [:person/name :person/yob]}]) (2)
1 | type :root means the key sits at the highest level of edn queries. |
2 | the table can have more columns, but that’s out of concern if you only want to give access to these very two columns. |
The query-env function
If you choose to have database-instance
under the key :db
of
the env
:
(require '[clojure.java.jdbc :as jdbc])
(def query-env #(jdbc/query (:db %1) %2))
(require '[next.jdbc :as jdbc]
'[next.jdbc.result-set :as rs])
(def query-env #(jdbc/execute! (:db %1) %2 {:builder-fn rs/as-unqualified-lower-maps}))
For Nodejs, you’ll need to convert between Javascript and Clojure data structure.
(ns your-ns
(:require [cljs.core.async :as async :refer [put! <! promise-chan]]
["sqlite3" :as sqlite3]))
(defn query-env
[env [q & params]]
(let [c (promise-chan)]
(.all (:db env) q (or (to-array params) #js [])
(fn callback [e r]
(let [x (js->clj r :keywordize-keys true)]
(put! c x))))
c))
The execute-query function
(Actually this execute-query
function is called parser
in
Pathom.)
Now that we have our registry
and query-env
, let’s wrap them with
some boilerplate so magic can happen :)
(def walkable-parser
(p/parser
{::p/env {::p/reader [p/map-reader
pc/reader3
pc/open-ident-reader
p/env-placeholder-reader]}
::p/plugins [(pc/connect-plugin {::pc/register []}) (1)
(walkable/connect-plugin {:db-type :postgres (2)
:registry registry
:query-env query-env})
p/elide-special-outputs-plugin (3)
p/error-handler-plugin]}))
(def walkable-parser
(p/async-parser
{::p/env {::p/reader [p/map-reader
pc/reader3
pc/open-ident-reader
p/env-placeholder-reader]}
::p/plugins [(pc/connect-plugin {::pc/register []}) (1)
(walkable/connect-plugin {:db-type :sqlite (2)
:registry registry
:query-env query-env})
p/elide-special-outputs-plugin (3)
p/error-handler-plugin]}))
1 | Pathom Connect config. |
2 | :db-type can be :postgres , :mysql or :sqlite . |
3 | Two other Pathom plugins. Let’s leave all these Pathom things as-is for now. Check out Pathom integration later. |
Run your queries
You make it! Bring your query and profit:
(walkable-parser
{:db some-db-instance} (1)
[{:people/list [:person/name :person/yob]}]) (2)
;; => {:people/list
;; [{:person/name "joe", :person/yob 1990}
;; {:person/name "eoj", :person/yob 2000}]}
(let [result (walkable-parser
{:db some-db-instance} (1)
[{:people/list [:person/name :person/yob]}])] (2)
(async/go (println (<! result))))
1 | the env hash map mentioned in Overview |
2 | the EQL query we designed in the beginning. |