Ring defines middleware as a function of type handler & args => request => response. It's relatively easy to understand and enables good performance. Downside is that the middleware-chain is just a opaque function, making things like debugging and composition hard. It's too easy to apply the middleware in wrong order.
Reitit defines middleware as data:
All values in the :middleware vector in the route data are expanded into reitit.middleware/Middleware Records with using the reitit.middleware/IntoMiddleware Protocol. By default, functions, maps and Middleware records are allowed.
Records can have arbitrary keys, but the following keys have a special purpose:
| key | description | 
|---|---|
| :name | Name of the middleware as a qualified keyword | 
| :spec | clojure.specdefinition for the route data, see route data validation (optional) | 
| :wrap | The actual middleware function of handler & args => request => response | 
| :compile | Middleware compilation function, see compiling middleware. | 
Middleware Records are accessible in their raw form in the compiled route results, thus available for inventories, creating api-docs etc.
For the actual request processing, the Records are unwrapped into normal functions and composed into a middleware function chain, yielding zero runtime penalty.
The following produce identical middleware runtime function.
(defn wrap [handler id]
  (fn [request]
    (handler (update request ::acc (fnil conj []) id))))
(def wrap3
  {:name ::wrap3
   :description "Middleware that does things."
   :wrap wrap})
(require '[reitit.middleware :as middleware])
(def wrap2
  (middleware/create
    {:name ::wrap2
     :description "Middleware that does things."
     :wrap wrap}))
:middleware is merged to endpoints by the router.
(require '[reitit.ring :as ring])
(defn handler [{:keys [::acc]}]
  {:status 200, :body (conj acc :handler)})
(def app
  (ring/ring-handler
    (ring/router
      ["/api" {:middleware [[wrap 1] [wrap2 2]]}
       ["/ping" {:get {:middleware [[wrap3 3]]
                       :handler handler}}]])))
All the middleware are applied correctly:
(app {:request-method :get, :uri "/api/ping"})
; {:status 200, :body [1 2 3 :handler]}
Middleware can be optimized against an endpoint using middleware compilation.
:requires and :provides. Values are set of top-level keys of the request. e.g.
InjectUserIntoRequestMiddleware requires #{:session} and provides #{:user}AuthorizationMiddleware requires #{:user}Ideas welcome & see issues for details.
Can you improve this documentation? These fine people already did:
Tommi Reiman & Marcus SpiegelEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs | 
| ← | Move to previous article | 
| → | Move to next article | 
| Ctrl+/ | Jump to the search field |