Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Functional Web Architecture
Search
weavejester
July 04, 2012
Programming
12
1.7k
Functional Web Architecture
Designing web applications with Ring and Compojure
weavejester
July 04, 2012
Tweet
Share
Other Decks in Programming
See All in Programming
PostgreSQLのRow Level SecurityをPHPのORMで扱う Eloquent vs Doctrine #phpcon #track2
77web
2
530
AI コーディングエージェントの時代へ:JetBrains が描く開発の未来
masaruhr
1
140
Modern Angular with Signals and Signal Store:New Rules for Your Architecture @enterJS Advanced Angular Day 2025
manfredsteyer
PRO
0
220
ISUCON研修おかわり会 講義スライド
arfes0e2b3c
1
440
PHPで始める振る舞い駆動開発(Behaviour-Driven Development)
ohmori_yusuke
2
390
A2A プロトコルを試してみる
azukiazusa1
2
1.4k
XP, Testing and ninja testing
m_seki
3
240
PicoRuby on Rails
makicamel
2
130
LT 2025-06-30: プロダクトエンジニアの役割
yamamotok
0
760
MDN Web Docs に日本語翻訳でコントリビュートしたくなる
ohmori_yusuke
1
120
20250628_非エンジニアがバイブコーディングしてみた
ponponmikankan
0
680
設計やレビューに悩んでいるPHPerに贈る、クリーンなオブジェクト設計の指針たち
panda_program
6
2.1k
Featured
See All Featured
Docker and Python
trallard
44
3.5k
[RailsConf 2023] Rails as a piece of cake
palkan
55
5.7k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.5k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
233
17k
Embracing the Ebb and Flow
colly
86
4.7k
Into the Great Unknown - MozCon
thekraken
40
1.9k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
2.9k
Building a Scalable Design System with Sketch
lauravandoore
462
33k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
357
30k
Building Flexible Design Systems
yeseniaperezcruz
328
39k
Code Review Best Practice
trishagee
69
18k
A Modern Web Designer's Workflow
chriscoyier
695
190k
Transcript
Functional Web Architecture Designing web applications with Ring and Compojure
twitter: @weavejester code: github.com/weavejester
Ring. What is it?
An abstraction over HTTP
What's HTTP?
A request / response protocol
HTTP Request Server Response
Ring user=> (server request) response
Ring user=> (handler request) response
HTTP Request GET /resource HTTP/1.1 Host: www.example.com
HTTP Request GET /resource HTTP/1.1 Host: www.example.com method URI headers
Request Map {:request-method :get :uri "/resource" :headers {"host" "www.example.com"}}
HTTP Response HTTP/1.1 200 OK Content-Type: text/plain Hello World
HTTP Response HTTP/1.1 200 OK Content-Type: text/plain Hello World status
body headers
Response Map {:status 200 :headers {"Content-Type" "text/plain"} :body "Hello World"}
Ring Map Function Map
No other abstractions
HTTP Request Server Response Ring Map Function Map
HTTP Request Server Response Ring Map Function Map Adapter
Example (defn handler [request] {:status 200 :headers {"Content-Type" "text/plain"} :body
"Hello World"})
Example (use 'ring.util.response) (defn handler [request] (-> (response "Hello World")
(content-type "text/plain")))
Example (use 'ring.util.response 'ring.adapter.jetty) (defn handler [request] (-> (response "Hello
World") (content-type "text/plain"))) (run-jetty handler {:port 3000})
Why abstract at all?
Power
Power to leverage Clojure
Middleware
Not a new abstraction Just higher order functions
Handler Middleware Handler Map Handler Map
Handler Request Response
Middleware Handler Request Response
Middleware Handler Request Response
Identity (defn wrap-identity [handler] (fn [request] (handler request)))
Caching (defn wrap-cache [handler] (fn [request] (or (get-cache request) (let
[response (handler request)] (put-cache! request response) response))))
Content Types (defn wrap-content-type [handler] (fn [request] (let [ext (get-ext
(:uri request)) type (ext-types ext)] (-> (handler request) (content-type type)))))
Parameters (defn wrap-params [handler] (fn [request] (let [query (:query-string request)
params (parse-params query)] (handler (assoc request :params params)))))
Ring core middleware • Query and form parameters • Multipart
file uploads • Cookies • Sessions • Content-types • Static resources
Routing Multiple resources from the same handler
Basic Routing (use 'ring.util.response) (defn handler [request] (case (:uri request)
"/hello" (response "Hello") "/world" (response "World") :else (not-found "Page not found")))
Compojure
Compojure (use 'compojure.core) (def hello-route (GET "/hello" request "hello"))
Compojure ; (GET "/hello" request "hello") (def hello-route (fn [request]
(if (and (= (:request-method request) :get) (= (:uri request) "/hello")) "hello" nil))))
Compojure ; (GET "/hello" request "hello") (def hello-route (fn [request]
(if (and (= (:request-method request) :get) (= (:uri request) "/hello")) "hello" nil))))
Compojure ; (GET "/hello" request "hello") (def hello-route (fn [request]
(if (and (= (:request-method request) :get) (= (:uri request) "/hello")) "hello" nil))))
Compojure ; (GET "/hello" request "hello") (def hello-route (fn [request]
(if (and (= (:request-method request) :get) (= (:uri request) "/hello")) "hello" nil))))
Compojure ; (GET "/hello" request "hello") (def hello-route (fn [request]
(if (and (= (:request-method request) :get) (= (:uri request) "/hello")) "hello" nil))))
Compojure (use 'compojure.core) (def handler (routes (GET "/hello" _ "hello")
(GET "/world" _ "world")))
Compojure (use 'compojure.core) (defroutes handler (GET "/hello" _ "hello") (GET
"/world" _ "world"))
GET /hello GET /world Routes Request Response
GET /hello GET /world Routes nil Request Response
GET /hello GET /world Routes nil Request nil
Compojure (use 'compojure.core) (require '[compojure.route :as route]) (defroutes handler (GET
"/hello" _ "hello") (GET "/world" _ "world") (route/not-found "Page Not Found"))
GET /hello route/not-found GET /world Routes nil Request Response nil
Compojure (use 'compojure.core) (require '[compojure.route :as route]) (defroutes handler (GET
"/hello" _ "hello") (GET "/world" _ "world") (route/not-found "Page Not Found"))
Compojure (use 'compojure.core) (require '[compojure.route :as route]) (defroutes handler (GET
"/:word" request (-> request :params :word)) (route/not-found "Page Not Found"))
Compojure (use 'compojure.core) (require '[compojure.route :as route]) (defroutes handler (GET
"/:word" {{word :word} :params} word) (route/not-found "Page Not Found"))
Compojure (use 'compojure.core) (require '[compojure.route :as route]) (defroutes handler (GET
"/:word" [word] word) (route/not-found "Page Not Found"))
Compojure (use 'compojure.core) (require '[compojure.route :as route] '[clojure.string :as str])
(defroutes handler (GET "/:word" [word] word) (GET "/:word/shout" [word] (str/upper-case word)) (route/not-found "Page Not Found"))
Compojure (use 'compojure.core) (require '[compojure.route :as route] '[clojure.string :as str])
(defroutes handler (context "/:word" [word] (GET "/" word) (GET "/shout" (str/upper-case word))) (route/not-found "Page Not Found"))
Compojure • Create handlers (GET, POST...) • Cascade handlers (routes)
• Nest handlers (context)
Compojure (wrap-authorize (context "/user/:uid" [uid] (let [user (get-user uid)] (routes
(GET "/" [] @user) (PUT "/" [& data] (put! user data)) (DELETE "/" [] (delete! user))))))
Compojure (defn rest-routes [model] (routes (GET "/" [] @model) (PUT
"/" [& data] (put! model data)) (DELETE "/" [] (delete! user))) (wrap-authorize (context "/user/:uid" [uid] (rest-routes (get-user uid))
What's the big idea?
Complex handlers can be a composition of smaller handlers
Function Orientated Architecture
What next? Content negotiation Finite State Machines REST abstractions Clojurescript
integration
Fin. Questions?