Liking cljdoc? Tell your friends :D

Extensions

Extensions let you add components to Zodiac's Integrant system and inject them into the request context.

What is an Extension?

An extension is a function that takes an Integrant configuration map and returns a modified configuration:

(fn [config] -> config)

Extensions are applied in order before the system starts.

Basic Example

Add a value to the request context:

(require '[zodiac.core :as z])

(defn my-extension [config]
  (assoc-in config [::z/middleware :context :app-name] "My App"))

(def routes
  ["/" {:get (fn [{::z/keys [context]}]
               {:status 200
                :body (str "Welcome to " (:app-name context))})}])

(z/start {:routes routes
          :extensions [my-extension]})

Adding Integrant Components

For stateful resources (database connections, caches, etc.), define an Integrant component:

(require '[integrant.core :as ig])

;; Define how to initialize your component
(defmethod ig/init-key ::database [_ {:keys [url]}]
  (create-connection url))

;; Optional: define cleanup
(defmethod ig/halt-key! ::database [_ conn]
  (close-connection conn))

(defn database-extension [config]
  (-> config
      ;; Add the component to the system
      (assoc ::database {:url "jdbc:postgresql://localhost/mydb"})
      ;; Inject into request context
      (assoc-in [::z/middleware :context :db] (ig/ref ::database))))

(z/start {:routes routes
          :extensions [database-extension]})

Now handlers can access :db from the context:

(defn handler [{::z/keys [context]}]
  (let [db (:db context)
        users (query db "SELECT * FROM users")]
    (z/json-response users)))

Extension Order

Extensions are applied in sequence. Later extensions can override earlier ones:

(defn ext1 [config]
  (assoc-in config [::z/middleware :context :value] "first"))

(defn ext2 [config]
  (assoc-in config [::z/middleware :context :value] "second"))

(z/start {:routes routes
          :extensions [ext1 ext2]})
;; :value will be "second"

Real-World Extensions

Zodiac SQL

Zodiac SQL provides database connectivity:

(require '[zodiac.ext.sql :as sql])

(z/start {:routes routes
          :extensions [(sql/extension {:dbtype "postgresql"
                                       :dbname "myapp"
                                       :host "localhost"})]})

;; In handlers:
(defn handler [{::z/keys [context]}]
  (let [users (sql/query (:db context) ["SELECT * FROM users"])]
    (z/json-response users)))

Zodiac Assets

Zodiac Assets integrates with Vite for asset bundling:

(require '[zodiac.ext.assets :as assets])

(z/start {:routes routes
          :extensions [(assets/extension {:manifest-path "public/.vite/manifest.json"})]})

;; In templates:
(defn layout [{::z/keys [context]} & body]
  [:html
   [:head
    [:link {:rel "stylesheet" :href (assets/url context "src/main.css")}]]
   [:body
    body
    [:script {:src (assets/url context "src/main.js")}]]])

Accessing Integrant Config

The full Integrant system is available if needed:

(let [system (z/start {:routes routes})]
  ;; system is the initialized Integrant system map
  (::z/app system)     ;; Ring handler
  (::z/router system)  ;; Reitit router

  ;; Stop the system
  (z/stop system))

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close