Clojure - Getting started with Liberator and TDD

Liberator

Liberator is a library that allows for creation of REST-based endpoints for construction of an API in Clojure. It has a simple set of functions that are quite powerful and allow for a great deal of customisation. It also has a slightly less simple decision graph that makes more sense with experience.

In this post we’ll put together a very simple endpoint, by starting first with a test and then progressing from there.

Getting Started

First off we’ll need to run:

Which will set up our project and give us the basics for running our code.

This gives us a failing test and a -main function that we can run.

Then, let’s add the dependencies to our project.clj so that they look like this:

This will give us the bare minimum libraries that we need to run a server that can accept and respond to requests.

Once we have our libraries we have somewhat of a chicken and egg problem, in that we need to do some basic setup before we can get testing working. First off we want to add a place for our resource that will be using liberator, that should look a bit like this:

And we’ll need to modify our core.clj to be able to start something that can respond to web requests. That should look something like this:

Since this is a more basic tutorial, while we’re here let’s talk about this in a little bit more detail, there’s essentially three parts to this file.

The core file

First off there are the imports, which are pretty standard. Because this is the core.clj generated for us by the lein command, we also have a :gen-class. We’re also using :require with :as to be able to alias the libraries, so we can be more specific in the body of the code, as well as giving meaningful names to things instead of long namespaces.

Then we have the main part of the file, where we define how to start a server. This uses Jetty as well as some ring middleware to be able to start up a server on port 3000 on localhost. There are a few parts to this that may not seem immediately obvious, but the key things to note are that we’re using the defroutes from our other file here, as well as :join? false which will help us out when we go to test our server.

The reason we have this as a separate function is so that we can call it from both this file, as well as from tests.

Finally we have the main function, which is our entrypoint for if we call lein run. For now for us, this just starts the server, but you could happily make this start a database or use something like components.

Putting the test in place

Now that we have all the scaffolding for the project, we can put a test in. Thankfully, Leiningen sets most of this up for you. You should currently be able to run lein test and see that the example test given fails. Hooray!

For this example, let’s assume that the data we want our endpoint to return is.

And as such let’s set up a test that looks for this. The first step is to allow us to call our webserver from a test. To do this we’ll use use-fixtures like so:

This lets us start up the server at the start of the test suite, runs the tests, and stops the server once our tests are done. This is a pretty common pattern in component and integration tests and because we’ve modularised the starting of our server, we can simply call start-server here from our core.clj and it will spin up a fresh server for us. In a more complicated setup you would likely want to have this on a random free port instead of using the port hardcoded in core.clj but, like most of this example, a lot of it can be swapped out later.

Once we have that, we can add our test, which as a whole file, should look like this:

Here we’re using http-kit to make a simple request to our endpoint, then we’re decoding the body with json/read-str and comparing it to our expected data.

Getting to green

Now that we have a red test, we’re going to want to turn it green, which in this case is actually a little simpler than the rest of the setup so far. We can make our resource.clj look like this:

Which adds our route to the make-routes function, and uses liberator to define the resource. Of particular note here is the :handle-ok which is how Liberator defines what will be returned for the OK status code. This can be a function, so you can have much more complicated implementations than this, but this makes a good start.

With this, we just need to run a lein test and our test should be green!

With such a simple example there’s not too much refactoring for us to do now, but we can definitely add more tests, like checking the status code of the response, or what happens if we hit an endpoint that isn’t yet set up.