Interceptors¶
com.walmartlabs.lacinia.pedestal2 defines Pedestal interceptors and supporting code.
The inject function (added in 0.7.0) adds (or replaces) an interceptor to a vector of interceptors.
Example¶
(ns server
(:require
[com.stuartsierra.component :as component]
[com.walmartlabs.lacinia.pedestal2 :as p2]
[com.walmartlabs.lacinia.pedestal :refer [inject]]
[io.pedestal.interceptor :refer [interceptor]
[io.pedestal.http :as http]))
(defn ^:private extract-user-info
[request]
;; This is very application-specific ...
)
(def ^:private user-info-interceptor
(interceptor
{:name ::user-info
:enter (fn [context]
(let [{:keys [request]} context
user-info (extract-user-info request)]
(assoc-in context [:request :lacinia-app-context :user-info] user-info)))}))
(defn ^:private interceptors
[schema]
(-> (p2/default-interceptors schema nil)
(inject user-info-interceptor :after ::p2/inject-app-context)))
(defn ^:private create-server
[compiled-schema port]
(let [interceptors (interceptors compiled-schema)
routes #{["/api" :post interceptors :route-name ::api]}]
(-> {:env :dev
::http/routes routes
::http/port port
::http/type :jetty
::http/join? false}
http/create-server
http/start)))
(defrecord Server [schema-source server port]
component/Lifecycle
(start [this]
(let [compiled-schema (:schema schema-source)
server' (create-server compiled-schema port)]
(assoc this :server server')))
(stop [this]
(http/stop server)
(assoc this :server nil)))
There’s a lot to process in this more worked example:
- We’re using Component to organize our code and dependencies.
- The schema is provided by a source component (in the next listing), injected as a dependency into the
Server
component. - We’re building our Pedestal service explicitly, rather than using
default-service
.
The interceptor is responsible for putting the user info into the request, and then it’s simple to get that data inside a resolver function:
(ns schema-source
(:require
[com.walmartlabs.lacinia.schema :as schema]
[com.walmartlabs.lacinia.util :as util]
[com.stuartsierra.component :as component]
[clojure.edn :as edn]
[clojure.java.io :as io]))
(defn ^:private resolve-user
[context _args _value]
(let [{:keys [user-info]} context]
;; Use user-info to get the data from somewhere ...
))
(defrecord SchemaSource []
component/Lifecycle
(start [this]
(assoc this :schema (-> (io/resource "schema.edn")
slurp
edn/read-string
(util/inject-resolvers {:queries/user resolve-user})
schema/compile)))
(stop [this]
(dissoc this :schema)))
Again, it’s a little sketchy because we don’t know what the user-info
data is, how its
stored in the request, or what is done with it … but the :user-info
put in place
by the interceptor is a snap to gain access to in any resolver function.
Tip
The inject
function is useful for making one or two small additions to the default interceptors,
but any more than that will likely lead to confusion about what order the items in the interceptor
pipeline are in; better to dupliciate the code from com.walmartlabs.lacinia.pedestal2/default-interceptors
instead.