A Simple Web Application with Clojure and CouchDB

By vijay on July 18, 2011 — 3 mins read

Here’s another very small step in my pursuit of learning Clojure. A couple of weeks ago Heroku introduced their new Cedar stack which adds Clojure support on their platform. With my usual curiosity for all things Clojury, I checked out their documentation on how to setup a simple web application written using Compojure and uses PostgreSQL as backend.

I succeeded in following the tutorial and made a simple working prototype even with my limited intelligence. But PostgreSQL seemed old-skool, since all the new cool kids are using NoSQL. So I started changing the backend to use CouchDB instead of PostgreSQL. So with great anticipation of criticism of my Clojure & CouchDB skills, I present how I did it.

The code I used as the starting point is the Shouter app which is explained in much detail in the Heroku’s documentation here. It is a “twitter-clone” (note the quotes!), which is built using Compojure and Hiccup. The code follows clear MVC pattern which made my task of switching the backend extremly easy.

Before I explain what I did in the code, first I need to give a heads-up on pre-requisites, there’s only one if you have a Clojure development installed, and that is CouchDB. Since we are planning to switch the backend to CouchDB the most important thing I needed was – CouchDB! So I went to the Couchbase site and downloaded the super-awesomely-quick-one-click Couchbase Server. This is just a small app you need to run which sits silently in your menubar running the CouchDB Server in the background. Very cool.

Once the couchbase server is ready, next step is to setup the project using lein using the command lein new couch-shouter. Once lein command finishes its business and creates the project, open the project.clj in Emacs. We need to add the required dependencies: clutch – for connecting to CouchDB, compojure & ring – to run the server, hiccup – for HTML generation. Here’s the project.clj in its entirety:

1

2

3

4

5

6

7

(defprojectcouch-shouter"0.0.1"

:description"Shouter App with CouchDB"

:dependencies[[org.clojure/clojure"1.2.1"]

[com.ashafa/clutch"0.2.4"]

[ring/ring-jetty-adapter"0.3.10"]

[compojure"0.6.4"]

[hiccup"0.3.6"]])

Once the dependencies are added, I had to make sure that they are loaded into my local lib directory by using the lein deps command. The core.clj file is the starting point of the application which provides the routes and contains the function that starts the jetty server.

Here’s the core.clj – the core of the web application

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

(nscouch-shouter.core

(:use[compojure.core:only(defroutes)])

(:require[compojure.route:asroute]

[compojure.handler:ashandler]

[ring.adapter.jetty:asring]

[couch-shouter.controllers.shouts]

[couch-shouter.views.layout:aslayout]))

;;; Define the routes

(defroutesroutes

couch-shouter.controller.shouts/routes

(route/resources"/")

(route/not-found(layout/four-oh-four)))

;;; the main application

(defapplication(handler/siteroutes))

;;; start to start the jetty server on port

(defnstart[port]

(ring/run-jetty(varapplication){:port(orport8080):join?false}))

;;; main

(defn-main[]

(let[port(Integer/parseInt(System/getenv"PORT"))]

(startport)))

The source for the Controller and the Views are pretty much copies of the original shouter code. Except that I’ve removed the CSS and other class declaration for simplicity. Here’s the shouts.clj controller:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

(nscouch-shouter.controller.shouts

(:use[compojure.core:only[defroutesGETPOST]])

(:require[clojure.string:asstr]

[ring.util.response:asring]

[couch-shouter.views.shouts:asview]

[couch-shouter.models.shout:asmodel]))

(defnindex[]

(view/index(model/all)))

(defncreate[params]

(let[shout(:shoutparams)]

(when-not(str/blank?shout)

(model/createshout)))

(ring/redirect"/"))

(defroutesroutes

(GET"/"[](index))

(POST"/"{params:params}(createparams)))

And here’s the listing of the views/layout.clj and views/shouts.clj

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

(nscouch-shouter.views.layout

(:use[hiccup.core:only[html]]

[hiccup.page-helpers:only[doctypeinclude-css]]))

(defncommon[title&body]

(html

(doctype:html5)

[:head

[:meta{:charset"utf-8"}]

[:meta{:http-equiv"X-UA-Compatible":content"IE=edge,chrome=1"}]

[:titletitle]]

[:body

[:div{:id"header"}

[:h1"COUCH SHOUTER"]]

[:div{:id"content"}body]]))

(defnfour-oh-four[]

(common"Page Not Found"

[:div{:id"four-oh-four"}

"The page you requested could not be found!"]))

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

(nscouch-shouter.views.shouts

(:use[hiccup.core:only[htmlh]]

[hiccup.page-helpers:only[doctype]]

[hiccup.form-helpers:only[form-tolabeltext-areasubmit-button]])

(:require[couch-shouter.views.layout:aslayout]))

(defnshout-form[]

[:div{:id"shout-form"}

(form-to[:post"/"]

(label"shout""What do you want to SHOUT?")

[:br]

(text-area"shout")

[:br]

(submit-button"SHOUT!"))])

(defndisplay-shouts[shouts]

[:div{:id"shouts"}

[:ul

(map

(fn[shout][:li(:data(:docshout))])

(:rowsshouts))]])

(defnindex[shouts]

(layout/common"COUCH SHOUTER"

(shout-form)

[:br]

(display-shoutsshouts)))

And now lets get to the nub of the matter, the model layer. First up is the model base, which provides the connection to the CouchDB Database:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

(nscouch-shouter.models.base

(:require[clojure.string:asstr]

[com.ashafa.clutch:asclutch])

(:import(java.netURI)))

(defndatabase-resource[]

(let[url(URI."http://127.0.0.1:5984/")

host(.getHosturl)

port(if(pos?(.getPorturl))(.getPorturl)443)]

(merge

{:hosthost}

{:portport}

{:language"Clojure"}

(if-let[user-info(.getUserInfourl)]

{:user(first(str/splituser-info#":"))

:password(second(str/splituser-info#":"))}))))

(defndb[]

(clutch/set-clutch-defaults!(database-resource))

(clutch/get-database"couch-shouter"))

As you can see above, the database-resource function is parsing the URI for the host, username, password, and other details needed for connecting to the database. There’s no username or password information yet, but if you are deploying this app to Heroku with Cloudant then you’ll get this information from the CLOUDANT_URL environment variable.

Anyway, let me show you how the shout model has been changed.

1

2

3

4

5

6

7

8

9

10

11

(nscouch-shouter.models.shout

(:use[couch-shouter.models.base:only[db]])

(:require[com.ashafa.clutch:asclutch]))

(defnall[]

(clutch/with-db(db)

(clutch/get-all-documents-meta{:include_docstrue})))

(defncreate[params]

(clutch/with-db(db)

(clutch/create-document{:dataparams})))

That’s it and the shouter app now runs with CouchDB. The source code of the app is now on Github at couch-shouter project.