In Tips for Clojure Beginners, Ben laid out some great resources for starting out with the language. Now, I want to focus on writing webapps in Clojure using Liberator. We started using Liberator as the backend for a JSON API and have had a good experience with it so far. So, I wanted to share a little bit about how to get started.
You have a few options for web frameworks with Clojure. We also considered Luminus, which looks like a great framework, but ultimately decided on Liberator. It seemed to align better with the single JSON API we are creating. Luminus seemed more in tune with creating full fledged app with a front end. Liberator’s documentation also provides a good [getting started]getting started overview which should give you a basic feel for how a Liberator app is setup.
Liberator provides functions for creating resources. These
resources, similar to resources in other web frameworks, can accept HTTP methods
defined in a vector of allowed-methods
. Here is a simplified definition for a
resource that accepts a GET
request:
(defresource bot [id]
:available-media-types ["application/json"]
:allowed-methods [:get]
:handle-ok (fn [_] (get-bot-by-id id)))
(defroutes post-routes
(ANY "/bot/:id" [id]
(bot id)))
Let’s decipher what is happening here. Our newly created resource is now called
from our defined routes. Currently we only have a single route. Liberator sits
on top of compojure which defines the ANY
and GET
functions for
our routes. Instead of specifying the GET
in our routes we pass any requests
to our resource. This way Liberator will check against our allowed-methods
and
return the appropriate 405 Method Not Allowed HTTP error code in the case that
it doesn’t contain the request type. This entry point to Liberator also provides
a good way for us to separate resources at the same endpoint if needed.
(defresource bots []
:available-media-types ["application/json"]
:allowed-methods [:get]
:handle-ok (fn [_] (get-all-bots)))
(defresource post-bots [params]
:available-media-types ["application/json"]
:allowed-methods [:post]
:post! (fn [_] (create-bot params)))
(defroutes post-routes
(POST "/bots" {params :params}
(post-bots params))
(ANY "/bots" []
(bots)))
In this example the POST
request to /bots
gets handled first by the
post-bots
resource while all other requests to the same endpoint get funnelled
through the bots
resource. This will then throw the appropriate error code for
PUT
and other unhandled methods.
So far we’ve seen post!
and handle-ok
which are both entry points where we
can override Liberator functionality in our resources. One of the most useful
resources while getting started with Liberator is its massive decision
graph. This graph lays out the entire state machine describing the flow
of requests through Liberator. The most useful part of this is that each node in
the graph is a possible entry point for your resource
s.
You can also see this decision list with the defaults and a little bit more explanation.
There is also a list of handlers, such as handle-ok
where you can
execute functions after a certain HTTP code has been decided upon by Liberator.
These can be very useful for deciding what to do after a request where you want
to return the representation of the created or updated entity.
You can create Liberator endpoints that are extremely flexible using these resources and overriding applicable entry points. Expect to see more posts about Liberator as we spend more time building out this API. In the meantime the Liberator documentation has a ton of very useful information as you get started.