« Post Index
Apr 7, 2013

Clojure web apps: Zero to 'Hello World' on Heroku in Clojure in 15 minutes.

I've been talking a lot about Clojure lately, but the pool of people who want to listen is pretty small. I know, I know, it's pretty different, but I promise it's really cool – maybe even better than the languages you already know – once you get to know it. So today I want to step back to the very basics by outlining how to get a Clojure app up and running, and maybe produce some more Clojurenauts in the process.

This article covers:

You can find all the code for this article at this Github repository.

Before you begin

You should know a little bit about Lisp syntax, and about Clojure's basic building blocks. A good place to get started is 4clojure. If you think you need to brush up, try completing all the “Elementary” problems.

If you don't already have a preferred editor capable of working with Clojure, give LightTable a try. It integrates with a repl to let you evaluate your code in-place as you write it – just press ctrl+enter (or cmd+enter) to evaluate the current statement.

Once you know what def, defn, fn, if and let do, you should be ready to go on.

TLDR

Here's where we're going:

; src/example/core.clj
(ns example.core
  (:use compojure.core)
  (:require
    [ring.adapter.jetty :as jetty]
    [clostache.parser :as clostache]))

; Template Rendering
(defn read-template [template-name]
  (slurp (clojure.java.io/resource
    (str "templates/" template-name ".mustache"))))

(defn render-template [template-file params]
   (clostache/render (read-template template-file) params))

; View functions
(defn index []
  (render-template "index" {:greeting "Bonjour"}))

; Routing
(defroutes main-routes
  (GET "/" [] (index))
  (route/resources "/")
  (route/not-found "404 Not Found"))

; Server
(defn -main []
  (let [port (Integer/parseInt (get (System/getenv) "PORT" "5000"))]
    (jetty/run-jetty main-routes {:port port})))

You can see it running in all its glory here

It might look a bit weird to you, but as you can see it's at least broken down into small bits. Lisps, and Clojure especially, really encourage this sort of thing. In Clojure you don't usually make some x and then do things to it line-by-line like you would in Java or Ruby or Python or PHP. Instead, you make a bunch of functions that take x and return a modified x, then chain them all together to accomplish the same thing. Except now, you have a bunch of tiny functions that are easy to test and reason about, at least once you get used to all the parentheses.

Ok, enough blathering. Let's get our hands dirty.

Step 1: Install Leiningen

Leiningen is to Clojure what Pip/Virtualenv are to Python, Gem/Bundle are to Ruby, or Maven is to Java. It's the standard for Clojure, and it's really easy to use.

The Leiningen Docs give the following instructions for installing:

  1. Download the lein script (or on Windows lein.bat)
  2. Place it on your $PATH where your shell can find it (eg. ~/bin)
  3. Set it to be executable (chmod a+x ~/bin/lein)

Step 2: Create a project

With Leiningen installed and in your path, you can use this command to create a new project

$ lein new example

The default project contains the following:

.clj files go in /src. Images, javascript, css, etc. go in /resources, usually /resources/public.

Step 3: Declare dependencies

Leiningen lets you add dependencies by simply adding them to your project.clj file.

To start, project.clj looks like this:

(defproject example "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]])

(You should fix all that stuff with FIXME in it.)

The last line contains the :dependencies declaration, which is a vector of vectors of the following format: [libraryname "version"]. You'll need to add the following:

Below :dependencies, you'll want to add a :main key with a value example.core. :main defines the name of the module containing a function called -main that you want to run. More on this later.

You also want to add :min-lein-version "2.0.0" to it somewhere, which is used when you deploy to Heroku.

Your project.clj should look something like this when you're done:

(defproject example "0.1.0-SNAPSHOT"
  :min-lein-version "2.0.0"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]
                 [ring "1.1.8"]
                 [compojure "1.1.5"]
                 [de.ubercode.clostache/clostache "1.3.1"]]
  :main example.core)

Leiningen will install these dependencies whenever you try to do something that needs them.

Step 4: Write your “Hello World”

Let's make the very smallest web-enabled applicatino we can. Open /src/example/core.clj and put the following in it:

(ns example.core
  (:require
    [ring.adapter.jetty :as jetty]))

(defn -main []
  (jetty/run-jetty
    (fn [req] {:status 200 :body "Hello World"})
    {:port 5000}))

How about some form-by-form commentary?

(ns example.core
  (:require
    [ring.adapter.jetty :as jetty]))

ns stands for “namespace”. Here you declare the name of this module and also list the imports.

Clojure has 3 styles of importing. use (or :use in a namespace) just drops whatever you import into the namespace. require (:require) will make you qualify your calls to the functions, as we will see. Here, require the ring jetty adapter. (The 3rd style is import, which is used to import java classes.)

(defn -main []
  (jetty/run-jetty
    (fn [req] {:status 200 :body "Hello World"})
    {:port 5000}))

This is the main function. It will start a webserver on port 5000, with a very minimal Ring handler attached to it. This is the handler:

(fn [req] {:status 200 :body "Hello World"})

A handler is just a regular clojure function that takes a request map and returns a response map.

At a minimum, your response should include a :status and a :body. You can also throw in :headers, with a nested map of headers as strings, like this:

(fn [req] {:status 200
           :body "{\"text\":\"Hello World\"}"
           :headers {"Content-Type" "application/json"}})

Step 5: Run it

Get to a command line, then run the following command from your project directory:

$ lein run

Since this is the first run, Leiningen will need to download all the dependencies before running. Once that's done and your server is up, point your browser at http://localhost:5000 and prepare to be amazed.

Step 6: Routing

Let's throw compojure into the mix. Compojure is a routing library that will make our app a bit easier to manage. Well, this app only has the one route, so it's actually not a big deal, but you'll thank me when you add more.

(defroutes main-routes
  (GET "/" [] "Hello World"))

The macros defroutes and GET come from compojure. defroutes is a macro that will create a ring handler from the contained statements. GET defines a routing. Here, we give GET a path, a list of arguments (which is empty), and a response, which will usually be the result of a function called with the given arguments.

The function in this case is just a string, “Hello World”. Compojure's route handler assumes that you mean for strings returned from view function to be wrapped with a :status 200 ring response, but you can return a response map yourself and Compojure will leave it alone.

Since defroutes produces a Ring handler, we can just replace our old handler with it:

(defn -main []
  (jetty/run-jetty main-routes {:port 5000}))

Step 7: Templates

You probably don't want to be putting html strings in your source, so you'll want some way of templating. I'm really enthusiastic about using Enlive for this, but for now we'll use Mustache, via the Clostache library I had you include earlier.

Clostache provides a function called render, which takes your template string, the parameters for rendering, and a map of partials that you may want to include.

To make it a bit more convenient, we'll write some wrappers. read-template will take a filename (with no extension) and read that file from /resources/templates. render-template will take a template file and a parameter map, and render it.

(In a real app, it may not be a bad idea to pre-load your templates and hold them in memory.)

(defn read-template [template-name]
  (slurp (clojure.java.io/resource
    (str "templates/" template-name ".mustache"))))

(defn render-template [template-file params]
  (clostache/render (read-template template-file) params))

Now you'll need to make a template. Try this:

<!-- /resources/templates/index.mustache -->
<html>
    <head>
        <title>Hello World</title>
    </head>
    <body>
        <div class="container">
            <h1>{{ greeting }}, World</h1>
        </div>
    </body>
</html>

Let's take this opportunity to move the view function out of the route definition as we put the rendering in. Notice we have to call index in the route definition.

; View functions
(defn index []
  (render-template "index" {:greeting "Bonjour"}))

; Routing
(defroutes main-routes
  (GET "/" [] (index)))

We use (index) instead of index because the expression in the route needs to be your response, not a function that returns it. That's why "Hello World" worked there before.

Resources

For a heavier deployment, you might want to serve your static files using some other method, but for the meantime (or for Heroku), we can serve static resources from /resources/public via our app.

Create the /resources/public folder, and create a the file /resources/public/style.css

body{
    font-family: "Arial", sans-serif;
}

.container{
    width: 600px;
    margin: 0 auto;
}

And adjust your template thusly:

<html>
    <head>
        <title>Hello World</title>
        <link rel="stylesheet" type="text/css" href="/style.css">
    </head>
    <body>
        <div class="container">
            <h1>{{ greeting }}, World</h1>
            <p>How do you do?</p>
        </div>
    </body>
</html>

Finally, use Compojure to set up static resource serving. As a bonus, we'll also add a 404 handler.

You'll need to require compojure.route for this, so add it to your ns at the top. It should now look like this:

(ns example.core
  (:use compojure.core)
  (:require
    [ring.adapter.jetty :as jetty]
    [clostache.parser :as clostache]
    [compojure.route :as route]))

Then, add the handlers to your route definition:

(defroutes main-routes
  (GET "/" [] (index))
  (route/resources "/")
  (route/not-found "404 Not Found"))

There! You can now put whatever you need to serve in the /resources/public directory.

As for serving from production, the easiest way is probably to keep everything in /resources/public in a subfolder. I ususally use css, img and js. Then, you can configure a web server to serve those folders directly, instead of making your app do the work.

Deploy to Heroku

Getting the heroku tools set up for your OS is beyond the scope of this article, but they sure make it easy to deploy once you have them.

Since Heroku defaults to lein trampoline run as its default method of running clojure apps, nothing needs to be changed there. We just need to modify -main to fetch PORT from the environment, which Heroku expects.

(defn -main []
  (let [port (Integer/parseInt (get (System/getenv) "PORT" "5000"))]
    (jetty/run-jetty main-routes {:port port})))

After that, it's just

$ heroku apps:create
$ git push heroku master

You can go to the url heroku generated for you to see your app in action when that's done.

Summary

Here's the core.clj file one more time. Notice the new index function, which calls render-template. This is usually considered better form than jamming function definitions right in your routes.

;; /src/example/core.clj
(ns example.core
  (:use compojure.core)
  (:require
    [ring.adapter.jetty :as jetty]
    [clostache.parser :as clostache]
    [clostache.route :as route]))

;; Template Rendering
(defn read-template [template-name]
  (slurp (clojure.java.io/resource
    (str "templates/" template-name ".mustache"))))

(defn render-template [template-file params]
   (clostache/render (read-template template-file) params))

;; View functions
(defn index []
  (render-template "index" {:greeting "Bonjour"}))

;; Routing
(defroutes main-routes
  (GET "/" [] (index))
  (route/resources "/")
  (route/not-found "404 Not Found"))

;; Server
(defn -main []
  (let [port (Integer/parseInt (get (System/getenv) "PORT" "5000"))]
    (jetty/run-jetty main-routes {:port port})))

Of course, we haven't done anything here that couldn't be approximately accomplished using Sinatra with:

get "\" do
  erb :index, locals: {greeting: "Bonjour"}
end

But I think there are some key differences:

Really, though, I'm not trying to sell you Clojure with this “Hello World”. All I want is for you to get confident enough to try it yourself. Nobody ever managed to explain to my why I should use Clojure; I only saw how excited people got about it after they used it, and I only understood that after giving it a go myself. I encourage everyone to do the same.

Further Reading

A special message

This is where the affiliate links live, but hear me out! I use these two services every day, and I wouldn't recommend them if I wasn't satisfied.

DigitalOcean - Purveyors of fine (and inexpensive) virtual servers. I use DigitalOcean to host Address Bin and a few others; it's my go-to host. Use this referral link for a $10 credit.

AirBnb - I've been living in AirBnbs for a few months now, and plan to for many more. If you've ever wanted to try them out, you can get a $25 discount from this referral link.