Developing Web Apps in Clojure

P. Madanasekaran

Introduction

One may ask what are the advantages in using Clojure for developing web-apps? There is a lot momentum around web development in Clojure as can be seen from the number of libraries emerging for various functionalities. A web application in Clojure is just a big function composed from many small functions. Unlike many platforms, such as Rails or Django, the Clojure web stack is not a single opinionated framework. Instead, you can put together a number of libraries to build your application. This bottom-up approach gives a lot of flexibility and one can use a library of his choice for various functionalities and combine them in different ways. The term “Framework-less” is used to describe this development.  Ring is a low-level interface and library for building web applications in the Clojure programming language. It is similar to the Ruby Rack library, the WSGI in Python, or the Java Servlet specification.

Ring:

Ring is the current de facto standard-base upon which web applications in Clojure are written. It is actually a wrapper around Java Servlet. The Ring library comes with a Jetty adapter which sits between a Jetty Servlet container and the rest of the application stack. The plugin (for dev) “lein-ring” uses the ring-server (also for dev) which in turns deploys everything on the embedded jetty-adapter. Now we will see this “lein-ring” the plug-in that provides some more functionalities than a bare adapter .It gives you more options like packaging your app as a standalone jar, war or enabling the development mode that automatically refreshes the Browser when source code is modified. You can start the embedded Jetty server by calling lein ring server.

The above figure-1 shows the Request-Response flow in a typical Ring app. The “lein-ring” adds some more functionality while calling the Jetty adapter.

Compojure:

Ring, as mentioned above, can be described roughly as a Clojure layer on top of the Java Servlet API  that gets translated into a set of Servlet Filters, Servlet mappings and configuration. One may just want to add Compojure on top of Ring, as without the routing macros a Ring app could end up in a giant mess Compojure is a routing library built on top of Ring. A number of other higher level libraries also are built on top of Ring. Compojure maps request-handler functions to specific URLs. The application sits on top of this stack, using these libraries to interact with the client and manage the application state. In other words higher level library/ frameworks such as Compojure use Ring as a common basis.  Ring abstracts the underlying HTTP implementations away from our code. Higher level libraries/frameworks built on top of it allow us to focus on writing our application using higher level abstractions instead of low-level code, as Ring handles all the nitty gritty HTTP implementation details, such as HTTP request/response, parameters, cookies, and so on.  This abstraction, coupled with the fact that Ring is built on top of the HTTP Servlet specification, enables us to package our application and host it in a variety of servlet containers, such as GlassFish, Tomcat and Jetty. We can also run our application as a standalone, which is actually the easiest and most popular way of running a web application written in Clojure and using Ring. This is possible thanks to the embedded Jetty server, which if we want could be swapped out for http-kit a highly efficient HTTP client/server for Clojure. The other choice is Immutant from JBoss. If you want to process the requests asynchronously or use WebSocket, you may have to use Http-kit or Immutant. It is reiterated that programming with Ring alone is rather low level as writing a Java Servlet and using a higher level library or framework along with it will provide us higher level abstractions to simplify our code. Look at Figure -2 and see the libraries fit together in a normal Clojure web application.

Leiningen  

 The requirements for creating a Clojure app are only Java and Leiningen, the build and dependency management tool of Clojure. Even the installation of the Clojure language is not necessary, as Leiningen will down-load the Clojure-version specified in the project.clj, the configuration file of a Clojure app, like any other library. Do the following, if you don’t have Leiningen already in your system. There is a windows installer but we will take the manual route and install Leiningen as shown below:

  • Download the Windows lein.bat. ( version > 2)
  • You can create a new folder in some directory for example F:\ lein\ and rename it as bin and copy your lein.bat there and edit your path variable pointing to the same.
  • In the command prompt, from there run the self-install package and a leiningen-standalone.jar will be downloaded and now your development environment is ready.

Samples

We can Create a new project with Leiningen with “lein new <some-template> app-name”. Look at the following commands.

  • lein new app myapp // creates a new Clojure app with main function
  • lein new myapp // creates a library – no main function. We can add Ring and other dependencies and make it a web application.  This approach gives us options in choosing a server from many available web servers and even in the way embedded Jetty is used. We will see this approach in the next article
  • lein new compojure myapp  // an easier way of creating a Clojure web app with Ring + Compojure

We will use the third option now and excute  
lein new compojure myapp

cd myapp
lein ring server-headless // headless to prevent default Browser opening a window automatically.
You can see a number of dependencies are downloaded and it includes Clojure, Compojure, a number of modules from Ring –ring core, ring header, ring-server, ring-jetty-adapter, ring-servlet etc- and the lein plugin for ring -lein-ring and ring-server. Finally embedded Jetty-server will be started and you will see the info “Started server on port 3000”

From this you can see that ring-server uses embedded Jetty server. Go to any Browser and navigate to
  localhost:3000
and see Figure-3 below;

Figure-3

First we can see the generated project.clj – the configuration file in the root folder of the project.

project.clj

(defproject myapp "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :min-lein-version "2.0.0"
  :dependencies [[org.clojure/clojure "1.7.0"] //--------1)
                 [compojure "1.4.0"]
                 [ring/ring-defaults "0.1.5"]]
  :plugins [[lein-ring "0.9.7"]]   //---------2)
  :ring {:handler myapp.handler/app} //-----3)
  :profiles
  {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]  //------4)
                        [ring/ring-mock "0.3.0"]]}})

  1. These are the project’s dependencies. They are written [GROUP-ID/ARTIFACTID "VERSION"]. If both the GROUP-ID and ARTIFACT-ID are the same, the GROUP-ID can be omitted.
  2. The :plugins keyword defines Leiningen plugin to activate in this project. The format is the same as for the :dependencies section discussed  above. In this case, lein-ring  provides some handy helpers for packaging and running Ring applications.
  3. The :ring section is the configuration for the lein-ring plugin. The “:handler” points to the top-level Ring handler that defines the application. The value is a namespace-qualified symbol. The part before the slash is a namespace, and the part after is the symbol bound to the application.
  4. Development dependencies include  javax servlet-api

Handler

What is handler? It is just a function that accepts a Request Map and returns a Response Map. This function returns a map that Ring can translate into a HTTP response. The response returns a plain text file. Apart from Request, Response and Handler, there is one important concept in Ring – Middleware, that is a higher level function that adds some functionality to handlers.

The generated handler passed to the :ring  key in project.clj is given below:

src/myapp/handler.clj

(ns myapp.handler  //-------1)
(:require [compojure.core :refer :all]
[compojure.route :as route]
[ring.middleware.defaults :refer [wrap-defaults site-defaults]]))

(defroutes app-routes    //--------3)
(GET "/" [] "Hello World")  //---5)
(route/not-found "Not Found"))  //------4)

(def app
(wrap-defaults app-routes site-defaults))   //------2)

  1. Namespace declaration maps to package structure. That is the file-system hierarchy under src exactly mirrors the namespace hierarchy.
  2. This is the handler referred in “:ring” section of project.clj. It defines the entry point, called app, through which all the requests to our application will be routed. The app() handler here just  provides a wrapper for the actual routes in the handler namespace. The site function is used to generate a Ring handler wrapped in middleware suitable for a typical website.
  3. As you know Compojure is a routing library built on top of Ring. Its defroute macro defines “app-routes” handler which contains two routes. The first handles the application. The second is a catch-all route for handling request URIs that haven’t been defined.
  4.  The catch-all route mentioned above.
  5. This is the route that handles application. It associates the URL and an HTTP method to a handler and generates a Response. The vector is meant for Request parameters .Here empty vector [] shows that there are no parameters are passed. The response body here is just a string. The route’s response will be automatically wrapped in the Ring response.

As the development mode is enabled you can change the response from “Hello World” to “Hello Ganesh” and refresh the Browser and see the following Figure-4

Figure-4

A bigger/full-fledged web application with view files
In the above sample there is no view file. Ring sent the string returned by the route as response. In a typical Clojure web apps the view namespace contains a common layout file which uses a HTML template library to generate HTML from Clojure data structures. The view folder/namespace is generated along with a layout file, when we generate new app using one of the following templates options: 1) compojure-app 2) luminus. The option “compojure-app”, apart from using compojure, uses Hicupp HTML template system for generating view layout files. The option “luminus”- which is micro-framework- is more advanced and uses apart from Compojure , a number of other libraries  for creating web-apps and services –for example  ClojureScript, a variant of Clojure that compiles into JavaScript for Clients to use,  and Django-like HTML template “Selmer”. We will use the first option compojure-app with lein new as shown below:
  lein new compojure-app  lrn_comp

The template will generate a running web application. You can run it using
lein ring server  
You can see some messages including

“lrn_comp is starting”
.......
“Started server on port 3000”

Project Directory

For the generated files and folders have a look at figure-5 below:

Figure -5

 In the above figure you don’t see a MVC application structure. There is a deviation from what we are used to, as Compojure does not follow MVC pattern and does not enforce any strict separation between the view and the controller. Instead, handlers are created for each application route. The handler processes HTTP requests from the client and dispatches actions based on them. The handlers drive the model that’s responsible for handling the domain logic. This approach provides a clean separation between the domain logic and the presentation layer of our application, without any further indirection. We can call this pattern MHV(Model Handler View).

The important files and folders are:

1)  project.clj -configuration file
Under  src/package
handler.clj- The hander is responsible for handling requests and responses.
Other major components that are present are:
•  routes — The routes contain the core of our application, such as the logic to render pages and handle client requests.
• model — This namespace is reserved for the data model of the application and the persistence layer.
• views — This namespace contains common logic for generating the application layout.

Configuration file. It is reproduced below:

project.clj

(defproject lrn_comp "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :dependencies [[org.clojure/clojure "1.6.0"]  // 1) dependencies include hiccup HTML template
                 [compojure "1.1.6"]
                 [hiccup "1.0.5"]
                 [ring-server "0.3.1"]]
  :plugins [[lein-ring "0.8.12"]]      
  :ring {:handler lrn_comp.handler/app  //-------2)
         :init lrn_comp.handler/init
         :destroy lrn_comp.handler/destroy}
  :profiles
  {:uberjar {:aot :all}   //--------3)
   :production
   {:ring
    {:open-browser? false, :stacktraces? false, :auto-reload? false}}
   :dev
   {:dependencies [[ring-mock "0.1.5"] [ring/ring-devel "1.3.1"]]}})   //--4) 

  •  The page body can be generated by any a template library with Compojure using a HTML template library- here hiccup is used.
  • The :ring section is the configuration for the lein-ring plugin. The “:handler”  points to the top-level Ring handler that defines the application.It also points to the init() and destroy() functions supplied by lein-ring. These are called when the application starts and shuts down. Any code that needs to be run on startup or shutdown should be called from these functions, respectively.
  • Creation of stand-alone jar file which can be run with java –jar command anywhere where java is installed is also enabled.
  • Required for hot reloading, so that you won't have to restart the server each time you make a change.

Handler in detail
The handler specified in the :ring section of the above configuration file can be seen in more detail. The code for handler.clj is given below:

src/lrn_comp/handler.clj

(ns lrn_comp.handler
  (:require [compojure.core :refer [defroutes routes]]         //--------1)
            [ring.middleware.resource :refer [wrap-resource]]
            [ring.middleware.file-info :refer [wrap-file-info]]
            [hiccup.middleware :refer [wrap-base-url]]
            [compojure.handler :as handler]
            [compojure.route :as route]
            [lrn_comp.routes.home :refer [home-routes]]))
(defn init []                                   
  (println "lrn_comp is starting"))

(defn destroy []
  (println "lrn_comp is shutting down"))
(defroutes app-routes     //------------2)
  (route/resources "/")                  
  (route/not-found "Not Found"))

(def app  
  (-> (routes home-routes app-routes)  //--3)
      (handler/site)    //--------4)
      (wrap-base-url)))

  • Have a look at “required files” –the files that are used here.
  • This defroutes macro defines some base routes for the application that aren’t related to any specific workflows. 1) a route for static resources and 2)  a catch-all route for handling request URIs that haven’t been defined.
  • The application specific routes are grouped in their own namespaces -the routes namespace, which we will see next.
  •  In other words the site function simply creates a handler wrapped in some common middleware that is suitable for a common website.

Routes

As mentioned above, the application specific routes are in home-routes (in routes/home.clj). The code for home.clj is given below.

src/lrn_comp/routes/home.clj

(ns lrn_comp.routes.home
  (:require [compojure.core :refer :all]   //------1)
            [lrn_comp.views.layout :as layout]))

(defn home []    //------3)
  (layout/common [:h1 "Hello World!"]))

(defroutes home-routes    //------2)
  (GET "/" [] (home)))   

  • All modules in compojure.core and views/layout.clj  are required
  • The defroutes macro defines routes for the application. Here home-routes has only one route which associates HTTP GET for the root url ( “/” ) to  “home” handler. The empty vector [] is used to bind path parameters. There can be more than one route here, especially a route for handling user input POST/.The defroutes macro that generates a Ring handler from the supplied routes.
  • The home handler uses common() function in layout.clj and passes it a vector containing a string to be sent in Response.

When you identify a specific workflow in your application, it makes sense to group all the logic relating to this workflow in a single place. The routes package in your application is reserved for the namespaces that describe these workflows. Since our application is very small, we define a single set of routes, along with some helper functions right in the lrn_comp.routes.home namespace. In an application that has more pages, we would want to create additional namespaces to keep the code manageable. We would then create separate routes under each namespace. But all routes will be grouped together in the handler namespace using the defroutes macro as shown in the handler.clj shown earlier. The routes macro will combine all the routes into a single set from which the final handler can be created. Since our app-routes contains (route/not-found "Not Found"), we should put it last, or the not-found route will prevent any routes defined after it from resolving.

View

This folder contains only layout.clj which contains the logic for generating a base/common layout file which is used by pages in the routes folder. That is the logic for generating individual pages is defined in routes that accept the request parameters and generate the appropriate response. Views namespace contains only common logic for generating the application layout. The page body can be generated by any a template library with Compojure using any of the following libraries :  Hiccup, Enlive, Selmer, and Stencil. Here Hiccup is utilized. Hiccup simply uses Clojure data structures to define the markup and generates the corresponding HTML from it. Hiccup  doesn’t necessitate learning any special syntax apart from using standard Clojure functions. Hiccup uses Clojure vectors with optional maps of attributes to represent HTML elements.

The layout.clj is reproduced below:

views/layout.clj

(ns lrn_comp.views.layout
  (:require [hiccup.page :refer [html5 include-css]]))

(defn common [& body]   //----1)
  (html5
    [:head   //-----------------2)
     [:title "Welcome to lrn_comp"]
     (include-css "/css/screen.css")]
    [:body body]))

  • “&” This symbol indicates what comes after this is a seq, though everything it is wrapped in a vector. If you include a Clojure seq in the body of an element vector, the seq is "expanded" out into the body.
  • The response map is created by the handler, and contains three keys a) :status. The HTTP status code, such as 200, 302, 404 etc. b) :headers. A Clojure map of HTTP header names to header values. These values may either be strings, in which case one name/value header will be sent in the HTTP response, or a collection of strings, in which case a name/value header will be sent for each value. c) :body A representation of the response body appropriate for the response's status code.

As mentioned above the views package is reserved for providing the layouts and other common elements for our pages. It comes pre-populated with the layout namespace. This namespace includes the common layout declaration to take care of generating the base page template for us. The common layout adds the head and title tags, includes resources such as CSS, and appends the content to the body. Since the content is wrapped using the html5 macro, an HTML string is automatically generated from the content when the common layout is called. This handler will send it to Compojure for generating HTTP Response.

Conclusion

As Clojure is a functional language, a Clojure Web application is created composing it from smaller libraries. Ring library provides the basic structure to combine the various libraries. While there are quite a few web libraries and even some web frameworks in Clojure, they all build on Ring’s foundations. Ring models HTTP requests and responses as data. This data is easily manipulated and transformed by all of the standard Clojure tools.

Using Ring as the basis for our web application has a number of benefits:
We can

  •  write our application using Clojure functions and maps
  •  run our application in an auto-reloading development server
  •  compile our application into a Java servlet
  •  package our application into a Java war file
  •  take advantage of a large selection of pre-written middleware
  • deploy our application in cloud environments like Amazon Elastic Beanstalk and Heroku.
  • create REST services that send JSON to the Browser.
  • use ClojureScript, a variant of Clojure, that compiles into JavaScript in place of JavaScript.  Why are we doing this? Because Clojure rocks, and JavaScript reaches.”-Rich Hickey
  • can access jdbc libraries  or  Korma or other DSL libraries and  with the databases through Clojure maps  and not through objects as Clojure does not have objects. There are libraries for interacting with Document Databases like MongoDB and CouchBase also.

In a language designed for the manipulation of data, elevating HTTP to the level of data gives web developers using Ring enormous amounts of power. Compojure is a routing library. When we use a compojure template we cannot see a MVC project structure. The major components that will be present in Compojure applications are 1) hanlder handling requests and responses.2) routes — The routes contain the core of our application, such as the logic to render pages and handle client requests.3) model is reserved for the data model of the application and the persistence layer.4) views contains common logic for generating the application layout.

Hiccup is one of the HTML template system that can be used with Compojure. It uses only Clojure data-structures and requires no knowledge other than using Clojure functions. In other words Hiccup is a library for generating HTML from Clojure data. It uses vectors to represent elements, and maps to represent an element's attributes. Incidentally James Reeves known as “weavejester” is one of the people associated with Ring and the creator of Compojure, lein-ring and Hiccup.  When we specify ‘compojure-app’ option to ‘lein new’, the code generated makes use of not only Compojure but also Hiccup HTML templates. Thus we get a minimal working web application. As development environment is enabled we can make changes to the code and refresh the Browser and see auto-reload in action. For more advanced web application or web services we can use the “luminus” template/ microframework. This template uses Django-like Selmer HTML template. For a full-fledged Clojure Web app created with Luminus, please refer to http://www.luminusweb.net/docs/guestbook.md.  There is a separate library Clojure  “core-async “ for reactive applications and some of the functions found there have been incorporated in the Clojure language in later versions . For a web services client, you can use ClojureScript, a Clojure variant that compiles into JavaScript and it will give the advantage of using the same language both in the server backend and the client.  React.js from Facebook is the latest technology that is making waves in the industry. It seems that it is easier to use ClojureScript with its wrapper Reagent and the build tool lein with React.js. The default is ES6, the new JavaScript standard that compiles into JavaScript. The ClojureScript with its functional paradigm, immutable data structures and controlled state management appears to be a better fit for React.js. We will see more about it later.








}