Liking cljdoc? Tell your friends :D

Middleware

Middleware describes how the nrepl server should respond to requests. It is designed to be modular so that servers can provide extra features without having to reimplement an nrepl server from scratch. Babashka's middleware implementation differs from the main nrepl implementation mostly to reduce dependencies.

Design

Middleware for babashka's nrepl server is implemented as a transducer. The goal of the transducer is to produce zero, one, or more responses for each input (ie. each incoming nrepl message).

Input format:

{:msg nrepl-request
 :ctx current-sci-ctx
 :opts options}

Output format:

{:response nrepl-response
 :response-for nrepl-request}

For a description of the nrepl-response and nrepl-request formats, check the nrepl documentation.

Usage

The most common types of middleware are implementing extra nrepl ops and cross-cutting middleware like logging. Below are examples of both of these common types of middleware.

Cross cutting middleware like logging


(require '[sci.core :as sci]
         '[babashka.nrepl.server.middleware :as middleware]
         'babashka.nrepl.server)

(def sci-ctx (sci/init {}))

(defonce responses (atom []))
(def
  ^{::middleware/requires #{#'middleware/wrap-response-for}}
  log-responses 
  (map (fn [response]
         (swap! responses conj (:response response))
         response)))

(defonce requests (atom []))
(def
  ^{::middleware/requires #{#'middleware/wrap-read-msg}
    ::middleware/expects #{#'middleware/wrap-process-message}}
  log-requests
  (map (fn [request]
         (swap! requests conj (:msg request))
         request)))

;; Add cross cutting middleware
(def xform
  (middleware/middleware->xform
   (conj middleware/default-middleware
         #'log-requests
         #'log-responses)))

(babashka.nrepl.server/start-server! sci-ctx {:host "127.0.0.1" :port 23456
                                              :xform xform})

Adding or overriding nrepl ops with middleware


(require '[sci.core :as sci]
         '[babashka.nrepl.server.middleware :as middleware]
         'babashka.nrepl.server)

(def sci-ctx (sci/init {}))

;; Add :foo and :baz ops
(def xform
  (middleware/default-middleware-with-extra-ops
   {:foo (fn [rf result request]
           (-> result
               (rf {:response {:foo-echo (-> request :msg :foo)}
                    :response-for request})
               (rf {:response {:bar-echo (-> request :msg :bar)}
                    :response-for request})))
    :baz (fn [rf result request]
           (-> result
               (rf {:response {:baz-echo (-> request :msg :baz inc)}
                    :response-for request})))}))

(defn responses [ctx xform msg]
  (transduce (comp
              (map (fn [msg]
                     {:msg msg
                      :ctx ctx}))
              xform)
             conj
             []
             [msg]))

;; Look at responses to a request with a :foo op
(responses nil xform
           {:op :foo
            :foo "hello"
            :bar "world"})
;; [{:response {:foo-echo "hello", "session" "none", "id" "unknown"},
;;   :response-for
;;   {:msg {:op :foo, :foo "hello", :bar "world"}, :ctx nil}}
;;  {:response {:bar-echo "world", "session" "none", "id" "unknown"},
;;   :response-for
;;   {:msg {:op :foo, :foo "hello", :bar "world"}, :ctx nil}}]

;; Look at responses to a request with a :baz op
(responses nil xform
           {:op :baz
            :baz 42})
;; [{:response {:baz-echo 43, "session" "none", "id" "unknown"},
;;   :response-for {:msg {:op :baz, :baz 42}, :ctx nil}}]

Advanced usage

Since middleware for babashka's nrepl is just a transducer, any transducer that produces nrepl responses for nrepl requests can be used. As a convenience, the default middleware is provided so that the transducer doesn't have to be built from scratch. The set of transducers can be found in the middleware namespace under babashka.nrepl.middleware/default-middleware. The default-middleware can be turned into a transducer using babashka.nrepl.middleware/middleware->xform. The benefit of using middleware->xform is that any transducers that have the meta data of :babashka.nrepl.middleware/requires or :babashka.nrepl.middleware/expects will be respected and the order of the transducers will be sorted accordingly.

Can you improve this documentation? These fine people already did:
Adrian Smith & Adrian
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close