(Todays) Adventures in Clojure
∞I’ve been writing a bit of Clojure again today, here are a few things I’ve learned today.
tl;dr: Look for middlewares in ring-defaults. ring-middleware-format is neat. Use cider in Emacs, and try again (and again).
Learning an ecosystem
It’s still hard to get started. I know Clojure (the language) well enough, but learning the tooling is much more difficult for me. I’ve tried to write simple APIs before, but the problems were similar each time.
Documentation
It seems that documentation for Clojure libraries is hard to find
on the web. ring
and compojure
both have generated docs, but
they are simply listings of the namespaces and the symbols in them,
without top-level examples.
So what I mostly did was a combination of reading examples, glancing at the source code of different libraries (ring-defaults, compojure-api, ring-middleware-format, …), and failing to get anywhere and trying again a few days later.
(I should have been using cider
s support for displaying
documentation more, but that wouldn’t have helped with discovering
which libraries to use.)
Middlewares?!
There’s a bewildering choice of ring
middlewares to try. ring
itself brings a lot of them with itself, but there are other useful
ones that come from other places.
However, finding them is mostly a matter of luck, I think. I’ve started out with ring-defaults, but it doesn’t do content negotiation and too much other things.
So now it’s just the following:
(def api
(-> handlers
(wrap-restful-format :formats [:edn :json :yaml-in-html])
wrap-keyword-params
wrap-params))
Where wrap-params
and wrap-keyword-params
come with ring itself,
and wrap-restful-format
does content negotiation.
To wrap-restful-format
uses the Accept
and Content-Type
headers
to decide how to interpret requests and responses. In your code you
simply set :body
to some data, and wrap-restful-format
will handle
the conversion.
How did I learn of wrap-restful-format
? I stumbled upon it while
trying out compojure-api,
which in turn I randomly found by searching for “compojure” on clojars.
I haven’t yet found a good way to catch errors. There are some middlewares for that, but I want one that does content-negotiation, and I don’t know if any support that.
Also, I haven’t yet found out how to selectively respond with HTML if requested, and otherwise API data. That would be very helpful for API endpoints that should also have a UI.
Neat things
This is mostly an aside, but both compojure-api and Nightlight
are neat projects. With compojure-api
you get automatic documentation
for your API, and can even try it out there easily. Nightlight gives
you an IDE in the browser. In theory that’s really cool, but it seems
to be lacking for documentation support at this point.
Assorted Emacs tips
By default, the macros from compojure
get indented very strangely,
but put-clojure-indent
can help. For example, to indent the GET
macro properly, use the following:
(put-clojure-indent 'GET '(:defn))
;; ... and so on for POST etc.
Here (:defn)
is an indent spec
which allows properly indenting even complex macros.
Another thing that often tripped me up were how comments are indented.
A single ;
comment is indented to the side.
This is the default and when using ;;
instead
the comment stays at the indentation level of the code surrounding it.
Updating the dependencies in project.clj
apparently does require
restarting the leiningen processes. In my case, that means rerunning
lein ring server-headless
and restarting the cider
connection
in Emacs.
Additionally, if you’ve not used cider for a while you may still have
its plugin in your ~/.lein/profiles.clj
file. This is not necessary
anymore.