Skip to content

Creating your own metadata handlers

Juho Teperi edited this page Dec 18, 2015 · 2 revisions

Compojure-api handles the route metadata by calling the multimethod compojure.api.meta/restructure-param with metadata key as a dispatch value.

Multimethods take three parameters:

  1. metadata key
  2. metadata value
  3. accumulator map with keys
    • :lets, a vector of let bindings applied first before the actual body
    • :letks, a vector of letk bindings applied second before the actual body
    • :middlewares, a vector of route specific middlewares (applied from left to right)
    • :parameters, meta-data of a route (without the key & value for the current multimethod)
    • :body, a sequence of the actual route body

.. and should return the modified accumulator. Multimethod calls are reduced to produce the final accumulator for code generation. Defined key-value -based metadatas for routes are guaranteed to run on top-to-bottom order of the so all the potential let and letk variable overrides can be solved by the client. Default implementation is to keep the key & value as a route metadata.

You can add your own metadata-handlers by implementing the multimethod:

(defmethod compojure.api.meta/restructure-param :auth
  [_ token {:keys [parameters lets body middlewares] :as acc}]
  "Make sure the request has X-AccessToken header and that it's value is 123. Binds the value into a variable"
  (-> acc
      (update-in [:lets] into [{{token "x-accesstoken"} :headers} '+compojure-api-request+])
      (assoc :body `((if (= ~token "123")
                      (do ~@body)
                      (ring.util.http-response/forbidden "Auth required"))))))

Using it:

(GET* "/current-session" []
  :auth token
  (ok {:token token}))

macroexpanding-1 it too see what's get generated:

(clojure.pprint/pprint
  (macroexpand-1 `(GET* "/current-session" []
                    :auth token
                    (ok {:token token}))))

(compojure.core/GET "/current-session" [:as +compojure-api-request+]
 (clojure.core/let [{{examples.thingie/token "x-accesstoken"} :headers} +compojure-api-request+]
  (do
   (if
    (clojure.core/= examples.thingie/token "123")
    (do (ring.util.http-response/ok {:token examples.thingie/token}))
    (ring.util.http-response/forbidden "Auth required")))))