prompts/skills/clojure-ring-core-middleware/SKILL.md
Ring core middleware for Clojure. Use when working with parameter parsing, sessions, cookies, flash messages, static files, or composing middleware stacks. Triggers on ring.middleware, wrap-params, wrap-session, wrap-cookies, or middleware composition questions.
npx skillsauth add ramblurr/nix-devenv clojure-ring-core-middlewareInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Ring middleware are higher-order functions that wrap handlers to add functionality. Middleware compose via function wrapping - outermost middleware executes first.
Common middleware stack:
(require '[ring.middleware.params :refer [wrap-params]]
'[ring.middleware.keyword-params :refer [wrap-keyword-params]]
'[ring.middleware.session :refer [wrap-session]]
'[ring.middleware.flash :refer [wrap-flash]])
(def app
(-> handler
wrap-keyword-params
wrap-params
wrap-flash
wrap-session))
Order matters: innermost (handler) to outermost (first to execute).
Parses URL-encoded parameters from query string and form body. Adds :query-params, :form-params, :params to request.
(wrap-params handler {:encoding "UTF-8"})
;; URL: /search?q=clojure -> {:params {"q" "clojure"}}
Converts parameter string keys to keywords. Must come after wrap-params.
(-> handler wrap-keyword-params wrap-params)
;; {"q" "clojure"} -> {:q "clojure"}
Converts flat params to nested structures using bracket notation.
(-> handler wrap-nested-params wrap-keyword-params wrap-params)
;; {"user[name]" "Alice"} -> {:user {:name "Alice"}}
Handles file uploads. Adds :multipart-params with file metadata.
(wrap-multipart-params handler {:store (temp-file-store)})
;; Uploaded file structure:
;; {"file" {:filename "doc.pdf" :content-type "application/pdf"
;; :tempfile #object[java.io.File ...] :size 51200}}
Options: :encoding, :store (temp-file-store or byte-array-store). Temp files deleted after 1 hour.
Parses cookies from headers. Adds :cookies to request, reads from :cookies in response.
(wrap-cookies handler)
;; Request: {:cookies {"session_id" {:value "abc123"}}}
;; Response: {:cookies {"session_id" {:value "abc123" :max-age 3600
;; :secure true :http-only true}}}
Attributes: :domain, :path, :secure, :http-only, :max-age, :expires, :same-site
Manages browser sessions via cookies. Read from :session in request, write to :session in response.
(wrap-session handler {:store (cookie-store {:key "16-byte-secret"})
:cookie-attrs {:max-age 3600 :secure true}})
;; Read: (get-in request [:session :username])
;; Update: (assoc response :session (assoc session :count 1))
;; Delete: (assoc response :session nil)
;; Recreate ID: (assoc response :session (vary-meta session assoc :recreate true))
Options: :store (memory-store default, cookie-store), :cookie-name, :cookie-attrs
Stores: memory-store (not multi-server), cookie-store (needs 16-byte key)
Session-based flash messages persisting across one redirect. Must wrap around wrap-session.
(-> handler wrap-flash wrap-session)
;; Set: (assoc response :flash "Success!")
;; Read: (:flash request)
Messages auto-expire after being read once.
Converts HEAD requests to GET and strips body. Usually innermost wrapper.
Returns 304 responses via ETag/Last-Modified. Checks If-Modified-Since and If-None-Match headers.
Auto-adds Content-Type from file extension. Falls back to application/octet-stream.
(wrap-content-type handler {:mime-types {"txt" "text/plain"}})
Auto-calculates Content-Length. Uses SizableResponseBody protocol.
Serves files from filesystem. Checks filesystem before handler.
(wrap-file handler "/var/www/public" {:index-files? true :allow-symlinks? false})
Serves resources from classpath (JAR/WAR compatible). Path relative to resources/.
(wrap-resource handler "public") ; serves resources/public/*
Combine with content-type and not-modified. These must wrap outside resource/file.
(-> handler (wrap-resource "public") wrap-content-type wrap-not-modified)
;; Basic pattern
(defn wrap-custom [handler]
(fn [request]
(let [response (handler request)]
response)))
;; Add request keys
(defn wrap-user [handler]
(fn [request]
(if-let [user-id (-> request :session :user-id)]
(handler (assoc request :user (get-user-by-id user-id)))
(handler request))))
;; Conditional execution
(defn wrap-auth [handler]
(fn [request]
(if (authorized? request)
(handler request)
{:status 403 :body "Access Denied"})))
(def app
(-> handler
wrap-keyword-params
wrap-nested-params
wrap-params
wrap-flash
wrap-session))
(def app
(-> handler
wrap-keyword-params
wrap-nested-params
wrap-params
wrap-flash
wrap-session
(wrap-resource "public")
wrap-content-type
wrap-not-modified))
Middleware order matters:
wrap-multipart-params doesn't include basic params - use both wrap-params and wrap-multipart-params
Session stores:
wrap-file-info is DEPRECATED - use wrap-content-type + wrap-not-modified instead
Flash messages require wrap-session
Cookie security: use :secure true for HTTPS, :http-only true to prevent JavaScript access, :same-site :strict for CSRF protection
File uploads: temp-file-store auto-deletes after 1 hour - save files permanently if needed
Custom session stores implement SessionStore:
(require '[ring.middleware.session.store :as store])
(defrecord CustomStore []
store/SessionStore
(read-session [_ key]
(read-data-from-backend key))
(write-session [_ key data]
(let [key (or key (generate-secure-random-key))]
(save-data-to-backend key data)
key))
(delete-session [_ key]
(delete-data-from-backend key)
nil))
CRITICAL: Generate cryptographically secure random keys for new sessions to prevent session hijacking.
testing
Use this OCP when executing or preparing to execute commands that change a live or important system, service reloads/restarts, package changes, deployments, migrations, firewall/network/access changes, credential rotation, NixOS switch/test/boot/deploy, or incident mitigation. It guides safe operations with a persisted ledger for scope, preflight, baseline, rollback, validation, and evidence.
development
Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, or build a new skill.
documentation
Naming conventions for workflow documents in prompts/. Use when creating plans, PRDs, research reports, idea capture or other workflow documents. Triggers on (1) creating new planning documents, (2) naming PRDs or research reports, (3) questions about document organization in prompts/.
testing
Grilling session that challenges your plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise. Use when user wants to stress-test a plan against their project's language and documented decisions.