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.

Require forms

  • Sync version

  • Async version

(require '[com.wsscode.pathom.core :as p]
         '[com.wsscode.pathom.connect :as pc]
         '[walkable.core :as walkable])
(require '[com.wsscode.pathom.core :as p]
         '[com.wsscode.pathom.connect :as pc]
         '[walkable.core-async :as walkable])

Define the registry

Let’s try the simplest example of the registry: Assume you have a table person. Now you want to give access to two columns name and yob in it under the keyword people/list.

(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.

You can learn about the registry here. For now the above code is all we need.

The query-env function

  • java.jdbc

  • jdbc.next

  • cljs+nodejs+sqlite

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))

Build the execute-query function

(Actually this execute-query function is called parser in Pathom.)

Now some boilerplate so magic can happen :)

  • Sync version

  • Async version

(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

Ready! Write your query and profit:

  • Sync version

  • Async version

(walkable-parser {:db some-db-instance}
  [{:people/list [:person/name :person/yob]}])
;; => {:people/list [{:person/name "foo", :person/yob 2000}]}
(let [result (walkable-parser {:db some-db-instance}
               [{:people/list [:person/name :person/yob]}])]
  (async/go (println (<! result))))