Writing web applications in Clojure
Functional Web
Clojure looks like Lisp and runs wherever Java is installed. Practical tools and sophisticated libraries provide the underpinnings for rapid development of modern web applications.
Clojure [1] made its first appearance in the software landscape in 2007. The functional programming language combines Lisp-like syntax with an implementation on the Java Virtual Machine (JVM). Within a few years, a Clojure community with user groups [2] and conferences (e.g., Clojure West [3] and Clojure/conj [4]) has been established.
An extensive ecosystem of libraries has emerged for the language, which is released under the Eclipse Public License. Clojure benefits from its Java origins, which lets it share the VM with Java and JRuby, and impresses with abstraction over concurrency [5] on multiprocessor systems. The name evokes the closure programming concept, with the "j" coming from Java.
Clojure advocates are enthusiastic about the improved productivity this expression-rich language permits. I strongly encourage you to take a look at an essay by Lisp advocate and entrepreneur Paul Graham [6] that describes the implications of the advantages Lisp programming confers, leading some Internet startups to focus on the language they use in large web applications. In this article, I show you how to use Clojure to implement a simple chat for the web browser (Figure 1).
Building with Leiningen
Most Clojure users rely on the Leiningen build system [7], which also takes care of installing the required libraries. If your package manager does not include it, you can follow the README file that comes with the source code for the install. The lein -v
command outputs the Leiningen version; when first called, the build system takes a few moments to download a few libraries. I used version 2.3.4 for this article, along with the most recent stable release of Clojure (1.5.1), with OpenJDK 7 as the Java implementation.
The project directory for the webchat is set by the
lein new webapp
command, which also sets up a skeleton for the application. Figure 2 shows the contents of webapp
: The project.clj
file contains information about the project and the components it needs – at present, only the current Clojure release.
Additionally, the src/
directory contains the first namespace of the webapp.core
application. Namespaces are the Clojure programmer's primary means of separating modules with different tasks. Each namespace is allowed to reference all objects or individual objects in other namespaces. However, it is possible to make certain objects private.
Interactive: REPL
The src/webapp/core.clj
file already contains a function. It can be called at the interactive command prompt, with which Clojure developers typically work, as well as in the editor. As usual in the Lisp family, the prompt is named REPL (Read Eval Print Loop). In the terminal, the developer types CD
to change to the project directory and then calls lein repl
.
The following REPL session uses the require
function and the in-ns
function to change into webapp.core
. Finally, it calls the foo
function with the argument "It works!"
:
user=> (require 'webapp.core) nil user=> (in-ns 'webapp.core) #<Namespace webapp.core> webapp.core=> (foo "It works!") It works! Hello, World! nil
A simple change to the source file demonstrates how REPL responds to changed code. In Listing 1 [8], the order of arguments in the println
function is reversed. After saving in the editor, reload the namespace in the same REPL to see the changed output:
Listing 1
src/webapp/core.clj
webapp.core=> (require 'webapp.core :reload) nil webapp.core=> (foo "It works!") Hello, World! It works! nil
This ability of REPL allows the developer to test code changes continuously. Editors like vim or emacs can integrate a REPL.
Bundled Libraries
The Clojure world is scarcely aware of large, monolithic frameworks. Most applications use a collection of small libraries, each of which serves a clearly limited purpose. Typical Clojure web applications rely on the Ring library, which represents an abstraction of the HTTP protocol. The sample application does not directly use Ring, however, but Compojure [9], which is based on Ring. On top of this, the project.clj
file defines some other libraries as dependencies (Listing 2).
Listing 2
project.clj Dependency List
Adding a new library is one of the few occasions the developer needs to restart the REPL. Although special tools can do this, entering Ctrl+D then lein repl
does the trick, and Leiningen automatically downloads the required files. This is followed by more changes to the core.clj
file in the editor. The following lines import functions from the Compojure library into the current namespace:
(ns webapp.core (:require [compojure.core :refer [defroutes GET]]))
This imports defroutes
and GET
from compojure.core
and makes them available for a first simple application:
(defroutes app (GET "/" [] "<h1>Hello World</h1>"))
Here, app
defines a Ring handler. It welcomes web surfers who access the /
address using HTTP-GET with a friendly "Hello World".
For this to work, the application requires an HTTP server, which is supplied by Httpkit [10] and is added to the required namespace in core.clj
:
(ns webapp.core (:require [compojure.core :refer [defroutes GET]][org.httpkit.server :as http]))
Now all functions can access org.httpkit.server
under the http/
prefix. This means you can try out the web server in the REPL:
user=> (require 'webapp.core :reload) nil user=> (ns webapp.core) nil webapp.core=> (def srv (http/run-server app {:port 8080})) #'webapp.core/srv
The http/run-server
function starts the application; def
binds to the return value to srv
. The browser application is now accessible under http://localhost:8080
. The function for launching the web server in turn returns a function. This can be checked in the REPL with the fn?
predicate. If you call the function itself, the server shuts down:
webapp.core=> (fn? srv) true webapp.core=> (srv) nil
The server can be restarted using (http/run-server app {:port 8080}))
without restarting the REPL, and you can even run multiple instances on different ports.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Juno Computers Launches Another Linux Laptop
If you're looking for a powerhouse laptop that runs Ubuntu, the Juno Computers Neptune 17 v6 should be on your radar.
-
ZorinOS 17.1 Released, Includes Improved Windows App Support
If you need or desire to run Windows applications on Linux, there's one distribution intent on making that easier for you and its new release further improves that feature.
-
Linux Market Share Surpasses 4% for the First Time
Look out Windows and macOS, Linux is on the rise and has even topped ChromeOS to become the fourth most widely used OS around the globe.
-
KDE’s Plasma 6 Officially Available
KDE’s Plasma 6.0 "Megarelease" has happened, and it's brimming with new features, polish, and performance.
-
Latest Version of Tails Unleashed
Tails 6.0 is based on Debian 12 and includes GNOME 43.
-
KDE Announces New Slimbook V with Plenty of Power and KDE’s Plasma 6
If you're a fan of KDE Plasma, you'll be thrilled to hear they've announced a new Slimbook with an AMD CPU and the latest version of KDE Plasma desktop.
-
Monthly Sponsorship Includes Early Access to elementary OS 8
If you want to get a glimpse of what's in the pipeline for elementary OS 8, just set up a monthly sponsorship to help fund its continued existence.
-
DebConf24 to be Held in South Korea
Busan will be the location of the latest DebConf running July 28 through August 4
-
Fedora Unleashes Atomic Desktops
Fedora has combined its solid distribution with rpm-ostree system to make it possible to deliver a new family of Fedora spins, called Fedora Atomic Desktops.
-
Bootloader Vulnerability Affects Nearly All Linux Distributions
The developers of shim have released a version to fix numerous security flaws, including one that could enable remote control execution of malicious code under certain circumstances.