Clojure All The Way - The Server Part 1 of mini-series
This is a first article in mini-series. The goal of mini-series is to create a Client-Server environment where Clojure data structures are pervasive and we don’t have to deal with JSON and JavaScript objects in Clojure/ClojureScript land (as much as possible).
In this post we’ll develop a Web server in Clojure (using Ring and Compojure) that exposes simple API to serve Clojure data structures in their natural text representation (fancy term for this is edn-encoded).
The Basics are not covered
The basics of creating Ring+Compojure Web server are covered very well on Interwebs. In this post I’ll only focus on creating API returning edn-encoded Clojure data.
Serving the site itself (static files)
Note: Code samples include only interesting parts, full source code is available below.
Let’s start with vanilla web server that simply serves static files:
1 (defroutes app
2 (GET "/" [] (file-response "default.html" {:root "html"}))
3 (files "" {:root "html"})
4 (not-found "<h1>404 Page not found</h1>")
5 )
- line 2 returns (not redirects to)
default.html
when server root is accessed - line 3 serves static files from “html” directory
- line 4 is only hit if static file is not found and returns 404
Adding Echo API
Now let’s add an echo
API that simply echoes back the client’s HTTP request.
To be precise: upon receiving HTTP GET /api/echo
request the server will respond with 200 OK
and body containing edn-encoded Clojure representation of request (a map of maps, strings and numbers).
Of cause we are not going to do heavy-lifting ourselves - that’s the whole point of Ring. To paraphrase Apple’s commercials “there is a middleware for that”. And it’s called ring.middleware.format-response
. Let’s use it:
1 (defroutes handler
2 (GET "/" ...)
3 (GET "/api/echo" request
4 {:status 200
5 :body request})
6 ...)
7
8 (def app (-> handler
9 wrap-clojure-response))
The app
was renamed to handler
(line 1) and new app
wraps this handler
into wrap-clojure-response
(lines 8-9).
The new Compojure route (lines 3-5) simply takes a request
(a Clojure map) and builds a 200-response from it setting :body
to request
(still a Clojure map).
And the wrap-clojure-response
middleware is the one converting Clojure map :body
returned by handler
to edn-encoded textual representation.
There is one problem with this code: :body
of request
that we are sending back is not a Clojure data structure. It’s a Java object of HttpInput org.eclipse.jetty.server.HttpInput
type. That’s not good: it cannot be edn-encoded and Client won’t be able to decode it anyway. A small tweak removes it from the response we are sending (call to dissoc
in line 3):
1 (GET "/api/echo" request
2 {:status 200
3 :body (dissoc request :body)})
Testing
To test our Server we can use a slightly modified client from my earlier post. And sure enough it shows client’s request returned back to us.
But ClojureScript Client still treats response as text (not structured Clojure data). We’ll deal with it in the next post.
Source code
Full source code can be found on GitHub.
If you want to build and run it locally, execute:
git clone https://github.com/Dimagog/dimagog.github.io.git -b ClojureAllTheWay1 --single-branch ClojureAllTheWay1
cd ClojureAllTheWay1
lein ring server
blog comments powered by Disqus