Middleware is code that wraps a handler function. This is usually done to either respond to the request before reading the wrapped handler or to parse information out of a request and make it available to that same handler.
This is all well and good, but if you start to have routes have their own bespoke stacks of middleware it can be a nightmare to debug and change later. Its especially onerous if you have to reason about "trees" of these middlewares
Therefore, I think it makes sense to take two pre-emptive steps
routes
namespace pick their own middleware from those standard sets.ring-defaults
to your deps.edn
.There is a pretty high chance that you might need to make your own "defaults" instead of purely relying on what this gives you. Its a decent starting point though.
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.12.0"}
ring/ring {:mvn/version "1.13.0"}
metosin/reitit-ring {:mvn/version "0.7.2"}
org.clojure/tools.logging {:mvn/version "1.3.0"}
org.slf4j/slf4j-simple {:mvn/version "2.0.16"}
hiccup/hiccup {:mvn/version "2.0.0-RC3"}
com.github.seancorfield/next.jdbc {:mvn/version "1.3.955"}
org.postgresql/postgresql {:mvn/version "42.7.4"}
com.zaxxer/HikariCP {:mvn/version "6.0.0"}
io.github.cdimascio/dotenv-java {:mvn/version "3.0.2"}
ring/ring-defaults {:mvn/version "0.5.0"}}
:aliases {:dev {:extra-paths ["dev" "test"]
:extra-deps {nrepl/nrepl {:mvn/version "1.3.0"}
lambdaisland/kaocha {:mvn/version "1.91.1392"}}}
:format {:deps {dev.weavejester/cljfmt {:mvn/version "0.13.0"}}}
:lint {:deps {clj-kondo/clj-kondo {:mvn/version "2024.09.27"}}}}}
src/example/middleware.clj
This takes in the system
as a matter of convention - some middlewares you write in the future
could want to access stateful things. Good example is authentication middlewares. Those might reasonably want
to make database queries.
(ns example.middleware
(:require
[ring.middleware.defaults :as middleware-defaults]))
(defn standard-html-route-middleware
[_system]
[#(middleware-defaults/wrap-defaults % middleware-defaults/site-defaults)])
After this we should be ready to actually process the data passed into the form.
(ns example.cave.routes
(:require [hiccup2.core :as hiccup]
[example.middleware :as middleware]))
...
(defn routes
[system]
[""
{:middleware (middleware/standard-html-route-middleware system)}
[["/cave" {:get {:handler (partial #'cave-handler system)}}]
["/cave/create" {:post {:handler (partial #'cave-create-handler system)}}]]])