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:
- Starting a new project with Leiningen
- Writing a Ring handler and running it.
- Routing with Compojure
- Rendering Mustache templates with Clostache
- Serving static resources with Compojure
- Deploying the above to Heroku
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]((http://leiningen.org/) give the following instructions for installing:
- Download the lein script (or on Windows lein.bat)
- Place it on your $PATH where your shell can find it (eg. ~/bin)
- 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:
project.clj
: The Leiningen configuration file.README.md
: A blank readme for your project/src
: A directory for your source files/resources
: A directory for your resources/doc
: For your documentation/test
: For your tests
.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:
[ring "1.1.8"]
: Run the server via Jetty[compojure "1.1.5"]
: Routing library[de.ubercode.clostache/clostache "1.3.1"]
: Mustache templates for Clojure
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:
- We got to choose our libraries, which are more-or-less interchangeable (with the exception of ring)
- We got to choose how to go about it, in a style that’s way more flexible than using pre-defined configuration on Sinatra
- We get a high-performance, threadable application by default.
- We get to use Clojure, and all the power to build the language to our problems that that entails.
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.