MixRadio's clojure skeleton project.
This generates the base REST service project that we use at MixRadio. In contains various libraries that we find useful and can be considered a condensing of the various pieces of commonality between our many clojure web services. We use this every day at MixRadio to power our music backend.
This project is the underlying Leiningen template, see ::TODO - add link to project:: for an example of the generated service.
Generate a new project with:
lein new mr-clojure <your-project-name>
You now have a web service with a couple of basic "I'm alive" resources and an embedded Jetty server to run it.
cd
into your new project directory and run
./acceptance wait
This starts the service in acceptance test mode but rather than running the tests and reporting the result, it just starts the service and waits. This allows you to manually call it or to run tests against it from the repl.
Let's call the healthcheck resource and see if we get a response:
curl localhost:8080/healthcheck
and we should see the result:
{"name":"i","version":"1.0.1-SNAPSHOT","success":true,"dependencies":[]}
Let's just add a quick example of a resource that responds to an input parameter. Leave the server running as we're going to do this live.
Open src/<your-project-name>/web.clj
and navigate to the route definitions at the bottom of the file. Add a new route on the resource /hello
and tell it to call a function called greet and take a parameter of name
. As a rule of thumb we like to keep our route definitions clean from too much code as it makes it very easy to see at a glace what a service does. The main way to do this to immediately defer to a handling function.
Here's how it should look:
(defroutes routes
(GET "/healthcheck"
[] (healthcheck))
(GET "/ping"
[] "pong")
(GET "/hello"
[nickname] (greet nickname))
(route/not-found (error-response "Resource not found" 404)))
And now let's define our greet function:
(defn greet
"Says hello!"
[nickname]
{:status 200 :body (format "Hello %s!\n" nickname)})
Save the file and give it a test:
curl localhost:8080/hello?nickname=world
Should give us the output:
Hello world!
Success!
There are a few different types of test defined in our skeleton project: unit, acceptance and integration. These words mean different things to different people and our definition is no exception so I'll define it here.
- Unit - This one is pretty well defined. However unlike a lot of people we often test our web layer heavily using unit like tests where we don't start the server. There are examples of this in
test/unit/web.clj
- Acceptance - Our definition here is probably one that suits our service oriented architecture. To us acceptance tests mean starting the service and poking it from another process but only in isolation. That it was use rest driver to mock out our calls to other web services. Some teams use this heavily where others lean more towards web unit test.
- Integration - Many people refer to this as an acceptance test. This is where the service is started and called with all its dependencies.
Each of these can be run in isolation using:
lein midje :filter unit
./acceptance
or ./acceptance wait
to only start the server and not run the tests
./integration
or ./integration wait
to only start the server and not run the tests
./all
to run all the tests
These are some of the libraries that we use:
- cheshire - excellent and fast JSON parsing and generation
- clj-http - a nice interface for making http calls
- clj-time - date and time library with a great interface
- clojure - 1.6
- compojure - the basis of our web service, used to define resources and middleware
- environ - reads environment variables and allows development values to be defined in the project.clj
- jetty - a lightweight JVM web server that we embed with our services
- midje - testing and mocking
- rest-driver - http level dependency mocking
lein release
will update the version number, uberjar and then create an RPM of the service. This RPM can be installed on any compatible server (e.g. Redhat, CentOs, Amazon Linux). You will, however need to ensure that the environment variables defined in the project.clj are available for the RPM to run.
Copyright © 2014 MixRadio