Getting Started with Liberator

Keith Smiley

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 resources.

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.