Web Development
39.1 Ring: The HTTP Abstraction
;; A Ring handler is a function: request map → response map
(defn handler [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body (str "Hello from " (:uri request))})
;; Middleware: functions that wrap handlers
(defn wrap-logging [handler]
(fn [request]
(println (:request-method request) (:uri request))
(handler request)))
(require '[ring.adapter.jetty :as jetty])
(def server (jetty/run-jetty (wrap-logging handler) {:port 3000 :join? false}))
;; (.stop server)39.2 Reitit: Data-Driven Routing
(ns myapp.web
(:require [reitit.ring :as ring]
[reitit.ring.middleware.muuntaja :as muuntaja]
[reitit.ring.middleware.parameters :as parameters]
[reitit.ring.coercion :as coercion]
[reitit.coercion.malli :as rcm]
[muuntaja.core :as mc]
[ring.adapter.jetty :as jetty]))
(def todos (atom {1 {:id 1 :title "Learn Clojure" :done false}}))
(def app
(ring/ring-handler
(ring/router
[["/api"
["/ping" {:get (fn [_] {:status 200 :body {:message "pong"}})}]
["/todos" {:get (fn [_] {:status 200 :body (vals @todos)})
:post {:parameters {:body [:map [:title :string]]}
:handler (fn [{{{:keys [title]} :body} :parameters}]
(let [id (inc (count @todos))
todo {:id id :title title :done false}]
(swap! todos assoc id todo)
{:status 201 :body todo}))}}]
["/todos/:id" {:get {:parameters {:path [:map [:id :int]]}
:handler (fn [{{{:keys [id]} :path} :parameters}]
…39.3 Ecosystem
HTTP abstraction + middleware Reitit / Compojure / Bidi
Jetty, http-kit, Aleph, Undertow (via ring-undertow)
Pedestal, Kit, Biff, Duct Frameworks / templates Muuntaja, jsonista Content negotiation, JSON Hiccup, Selmer
HTML generation (data → HTML), Django-like templates Authentication, authorization, crypto, JWT
clj-http, hato, http-kit client
(require '[hiccup2.core :as h])
(str (h/html [:div {:class "card"} [:h1 "Hello"] [:ul (for [x ["a" "b"]] [:li x])]]))
;; "<div class=\"card\"><h1>Hello</h1><ul><li>a</li><li>b</li></ul></div>"