Search code examples
clojurebindingcompojurecompojure-api

How do I bind a dynamic variable?


How do I bind a dynamic variable in compojure? Please see my example below, here the request-id is a unique uuid which is generate for each api request. I would like to be able to access this request-id in subsequent methods for logging etc. I have tried using the binding function, but am still not able to access request-id in some-page/some-method.

handler.clj

(ns some_app.handler
  (:require
    [compojure.api.sweet :refer :all]
    [compojure.route :as route]
    [some_api.some_page :as some-page]))

(def ^:dynamic *request-id*
  nil)

(defn ^:private generate-request-id []
  (str (java.util.UUID/randomUUID)))

(def app
  (binding [*request-id* (generate-request-id)]
    (api
      (context "/api" [] (GET "/some-page" [] (some-page/some-method))))))

some-page.clj

(ns some_app.some_page
(:require
        [clojure.tools.logging :as log]))

(def some-method []
  (log/info {:request-id *request-id*}))

Solution

  • The call to binding here is in the wrong place. The binding should be in effect when the request is processed, not when the app/api is being constructed.

    You want to have some middleware to do this:

    (defn with-request-id 
      [f]
      (fn [request]
        (binding [*request-id* (generate-request-id)]
          (f request)))
    
    (def app
      (with-request-id
        (api ... ))
    

    See also Ring Concepts