Single Page Application Overview

The single page application (SPA) approach has revolutionized our approach to development.  Fantastic client-side Javascript frameworks like AngularJS, Knockout, and React have made the transition easy and fun.  As we’ve written more and more SPAs an unexpected trend has emerged.  We’ve moved to a thin server architecture for delivering our back-end solutions on SPA projects.  We increasingly think of the application tier as plumbing, only responsible for data storage with a little authentication, authorization, and validation thrown in.

We’ve built thin SPA back-ends in Microsoft ASP.Net MVC, ASP.Net Web API, NodeJS with Express, and Ruby with Sinatra and Rails.  As the back-end has lost logic and responsibilities we care less and less about which technology stack we select for the solution.  Having covered three of the biggest interpreted runtimes in the CLR, V8, and Ruby, we recently wanted to deliver a back-end solution for the JVM.  We reviewed many options like jRuby and Scala, but selected Clojure.  We feel Clojure’s functional programming and data structure philosophy are a better match for our style of thin server architectures.  Clojure checks the box as a solution we can deploy on the JVM, and in this article we’ll explore our experience building a simple API to store and load JSON documents.

As an added benefit, Clojure’s immutable data structure philosophy has influenced Om, a high-performance client-side rendering framework based on React.  In our second article we’ll demonstrate a simple SPA using Om building upon the Clojure JSON REST API presented here.

CouchDB

We wanted the data storage back-end for this example to complement Clojure’s focus on data-structures.  As a document store, CouchDB fits nicely in our example since it maps directly to and from Clojure’s lists, sets, and maps.  Just as single page applications have reduced our application-tier complexity, they have also changed our approach to data modeling.  Our SPA front-end is based on Javascript, and as a dynamic language, Javascript is far more accepting and malleable to change in our business data models.  SPAs have freed us from statically typed rendering logic and boiler plate domain to data transfer object (DTO) mapping code.  We’ll discuss light-weight data modeling more in a future article.

Getting Started

Let’s get started with the project.  First we need some prerequisites: Leiningen, JDK, CouchDB, and a Clojure-friendly editor.  We selected Light Table as our editor and have been pretty happy with it so far.  We started with a default Ring server running on Jetty and updated our project.clj to include several Clojure libraries (compojure, ring-json, ring-core, and clutch):

:dependencies [[org.clojure/clojure "1.6.0"]
               [compojure "1.1.8"]
               [ring/ring-core "1.2.1"]
               [ring/ring-json "0.3.1"]
               [com.ashafa/clutch "0.4.0-RC1"]]

Clutch wraps the CouchDB interface for easy consumption in Clojure.  We decided to use the “experimental” interface, but since it hasn’t been actively developed since 2012 we may switch back to the main Clutch API…

Next modify your handler.clj file so the require sections reads like so:

(:require [compojure.core :refer :all]
          [compojure.handler :as handler]
          [compojure.route :as route]
          [ring.middleware.json :as middleware]
          [ring.util.response :refer [resource-response response]]
 )

We then define two routes:

 (GET
   ["/test/:id" :id #"[0-9]{1,4}"] [id]
   (load-record id))

 (POST "/"
   {body :body}
   (save-record body))

We also changed our app to wrap some middleware around Ring.  With this change we can receive JSON as the body of an HTTP POST.

(def app
 (-> (handler/api app-routes)
 (middleware/wrap-json-body)
 (middleware/wrap-json-response)))

In our methods.clj file we added the following:

(ns firstweb.views)
(require '[com.ashafa.clutch :as clutch])

(defn open-connection []
  (let [conn (doto
               (clutch/couch "test")
               (clutch/create!))]
    conn))

(def db-connection (delay (open-connection)))

(defn db [] @db-connection)

(defn load-record
  [id]
  {:body (get-in (db) [id])})

(defn save-record
  [data]
  (let [id (get-in data ["_id"])]
    (clutch/assoc! (db) id data)
    {:body (get-in (db) [id])}))

Launch our Single Page Application back-end

With this code in place we fire up our server in leiningen and then proceeded to construct a post via Postman:

Sending JSON REST API Request with Postman

And our response:

Single Page Application JSON REST API Response

Now we can request the JSON from our Clojure WEP API via an HTTP GET, http://localhost:3000/test/2014, shown from Google Developer Tools:

Single Page Application Google Developer Tools Response

Futon

Before we finish we thought it would be great to throw out a mention on CouchDB’s fantastic Web UI explorer, Futon.  Futon allows us to browse and make changes to our CouchDB data, as well as construct map-reduce results.  Here are some screenshots showing it in action:

Single Page Application CouchDB Futon Document

Now for a quick map-reduce to find the total goals scored in our incomplete sample 2014 World Cup results:

Single Page Application CouchDB Futon Map-Reduce

Conclusion

We hope you have enjoyed a quick overview of using Clojure and CouchDB as a back-end for a Single Page Application. In our next article we will demonstrate how Om and ClojureScript can be used in the SPA client to create some incredible performance gains for complex rendering scenarios.

TwitterLinkedInFacebookStumbleUponEmail

Brett founded the BHW Group in 2005 and currently serves as the company’s Principal and Chief Executive Officer (CEO). Brett leads a talented team at BHW, specializing in web and mobile application solutions for businesses of all sizes.

  • Michael_Sw

    Thanks for sharing, that’s quite cool. I look forward to the sequel, I really liked David Nolen’s video on Om but I haven’t had the energy to do a deep dive into it yet.