Deploying a Clojure web app on Heroku

Heroku is a cloud application platform for Ruby/Rails and Node.js. However, the Cedar stack on Heroku makes it possible to deploy other types of applications. In this blog entry, I will first describe how to write a simple Clojure web app using the Ring library and the build tool Leiningen. Then I will show how to deploy this Clojure web app on Heroku, using nothing but Git. I will make a change and see how to deploy that. I will also show how to easily roll back to a previous release.

Clojure maps

A map is a data structure that associates keys to values. Maps are very important in Clojure, because they are used to store entity data, much like custom classes with fields and setters/getters are used in traditional object-oriented languages like Java. In the Java world, we have java.util.Map. They are somewhat cumbersome to work with, mostly due to the static, generic typing, but also because there is no literal map syntax, plus the fact that you can’t initialize it with elements. You must first create it, then mutate it:

Clojure maps, on the other hand, have a very concise literal syntax. Zero or more key-value pairs between curly braces. That’s it.

The key :somekey in the map above is a Clojure keyword. Keywords are symbolic identifiers that always evaluate to themselves, and they have a very fast equality check. For those reasons (and others, as we will see), keywords are often used, rather than strings, as keys in maps.

A Clojure map is not only a data structure, it is also a function. When a map is called with a key, it will return the corresponding value (or nil if no key was found). It can look like this when testing it from the REPL:

In fact, keywords are functions too. When a keyword is called with a map as argument, it will look itself up in the map and return the matching value:

This form is more common in idiomatic Clojure.

The Ring library

Ring is a Clojure web applications library inspired by Ruby’s Rack. Ring abstracts the HTTP request and response as maps, and expects handlers to be functions that take a map as argument and returns a map. The returned map is transformed by Ring into a HTTP response, which is sent back to the caller. This is a simple, but powerful abstraction. It means that web handlers can be written as regular functions, which can be tested in isolation. Web libraries like Compojure build on top of the Ring abstraction and provide more high-level constructs, like routing. In order to not complicate this example, however, we will stick to plain Ring.

Let’s say that we have a HTTP request that looks like this:

Ring transforms this into a Clojure map that looks like this:

Let’s further assume that we want to generate a corresponding HTTP response:

In order to produce that HTTP response, Ring expects this map:

So all we need to do is to provide a function that returns the above map, like this:

Ring provides a response convenience function that returns a skeletal response map with status 200, no headers, and the given argument as body:

We’ll use the response function in our example later.

Leiningen

In order to manage our dependencies, we will use the Leiningen build tool. The simplest way to install it is to download the stable release of the script and place it somewhere in the PATH, like ~/bin. Then run lein self-install:

Building the web application

We create a new project and go there:

The newly created directory contains the following files:

First we need to create a Procfile for Heroku:

This will have Leiningen start the web application by running the function called -main in the namespace cljheroku.core, ie the file src/cljheroku/core.clj. We’ll change that file to contain this:

Let’s look at this code in more detail. We begin with the first chunk:

The ns macro will create a namespace cljheroku.core and load all functions in the namespaces ring.util.response and ring.adapter.jetty. Since we used :use and not :require, these functions can be referenced by name, without any prefix or namespace qualifier. This enables us to write (response ...) instead of (ring.util.response/response ...).

Then we define our Ring handler called app.

As you can see, it’s just a regular function that takes one argument: a HTTP request map, and returns the result of the response function: a HTTP response map. If we wanted to set the Content-Type header, we could use the content-type function, another function from ring.util.response. Instead of (response "Hello world"), we would pass the response through the content-type function: (content-type (response "Hello world") "text/plain"). But here we will just use response.

Finally we specify a -main function.

It will serve the same purpose as the Java main method, namely provide an entry point from the outside world. It gets the value of the PORT environment variable, or uses 8080 as default. It can be enlightening to analyze that part in detail. (System/getenv) is simply Clojure’s way of calling Java’s static method System.getenv(), which returns a java.util.Map. The get function takes a map (yes, it works with Java maps too), a key and a default value, and returns the matching value. So, (get (System/getenv) "PORT" "8080") will get the PORT variable from the environment, and if there is no PORT set there, it will return “8080″. The resulting string value of the port is parsed into an integer, and is stored as the local variable port. This value is then passed in to the Jetty adapter as part of a configuration map. All this in two lines. If you’re not used to Clojure’s lack of ceremony, you might find it overwhelming. I personally find it refreshingly to-the-point.

OK, that was the source code for our handler. We now need to adjust the project.clj file slightly, and provide a dependency to Ring. We’ll also add an exclusion of a duplicate dependency that Jetty has. Here is the resulting project.clj:

Before we do anything else, though, we should add this baby to Git:

We should probably push this to a maintained repository somewhere, but for our purposes, we have now stored this in Git.

Testing locally

Before we test it, we will ask Leiningen to retrieve the dependencies:

The lib directory now contains a list of jars that we need. Leiningen will make sure they all get on the classpath without you ever having to see it. You can ask Leiningen for the classpath, though, should you really like to see it:

You can also have Leiningen create a pom file for you, which can be handy if you need to look at the dependency:tree, import the project into an IDE that only knows Maven; things like that:

Anyway, we can now start this web application using the same command as we placed in the Procfile for Heroku. It will start Clojure with the correct classpath, find and make available the given namespace (cljheroku.core), and call its -main function with any given arguments (none, in our case):

Browsing to localhost:8080 shows a page with “Hello world” on it. Good. It works.

We now change the handler function to instead respond with, say, “Hello everyone” and save it. When we reload the page, the new message appears. Excellent. It reloads automatically.

OK. We’re happy with our web application and want to deploy it into the cloud. What do we do?

Deploying on Heroku

Heroku is, as I mentioned at the beginning, mainly a Ruby application platform. The command line tool for working with Heroku is a Ruby Gem. We first need to install Ruby and RubyGems. Assuming that has been done, we need to get the heroku gem:

Now we can create an app on the Cedar stack:

We can list the remote repositories to see what the last sentence above meant:

And finally, in order to deploy our first verison of the web app, we push everything to heroku:

We browse to http://furious-sword-794.herokuapp.com, and we see the text “Hello world” (if you remembered to change back from “Hello everyone”). Now we make a small change:

All we need to do to deploy the change is to commit it, and then push to heroku:

When we reload the page, the new text is there.

Roll back to previous release

We can list the releases that have been pushed:

Let’s say there was a problem with the release that was just deployed, and we want to roll back. With Heroku, it’s trivial to roll back to the previous release:

When we’re done with this web app completely, we can destroy it like this:

It’s even possible to roll back to earlier releases, but I won’t show that here. This concludes the Clojure-on-Heroku guide. Thank you for your time.

References

  1. Heroku: http://www.heroku.com/
  2. Clojure: http://clojure.org/
  3. Ring library: https://github.com/mmcgrana/ring
  4. Leiningen: https://github.com/technomancy/leiningen
  5. Compojure: https://github.com/weavejester/compojure/wiki
  6. FlightCaster uses Clojure and Heroku: http://www.infoq.com/articles/flightcaster-clojure-rails
  7. I got the idea for this blog from this Gist: https://gist.github.com/1001206

5 Comments

  1. Thanks for a very good article!

  2. Donald Parish

    Thanks! This article got me to fire up my Heroku account, that had been sitting idle since my last heroku app using ruby.

  3. I’ve been playing with a Clojure/Ring webapp with a view to deploying on AWS, but heroku looks like a very tempting alternative.

  4. Thank you, guys. You’ll appreciate the recently announced official support for Clojure on Heroku, as described here: http://www.jayway.com/2011/07/07/clojure-third-language-officially-supported-on-heroku/

  5. Greetings! Very helpful advice in this particular post! It’s the little changes which will make the greatest changes. Thanks for sharing!

Leave a Reply