Developing a web app with Clojure server and React UI

Tim Robinson
4 min readFeb 28, 2021

If you google for Clojure and React, you’ll mostly only find information about using ClojureScript in the browser and I couldn’t find anything about using a Clojure server as the back-end to a React app, even though it seems like a perfectly reasonable thing to do. In my case, I was converting the back-end of an ASP.NET/React app to Clojure as a learning exercise so I wanted to keep my existing React code.

This is part one of a two-part blog. The second part deals with how to build the app for production and deploy it into Microsoft Azure

A quick caveat before we get into it — This is definitely not a tutorial. To make sense of it, you’ll need to know the basics of React and Clojure (including ring and compojure routing).

If you’re familiar with React (or any other JavaScript framework), you’ll know that they generally require a hugely complex build process involving pre-processors, bundlers, babel fish, etc., so like many others I chose to use create-react-app which gives you a pre-packaged React app template “fully loaded” with every conceivable option.

There are two downsides to this approach: The first is that if you want to tweak or troubleshoot the process, it’ll be much more complicated than if you’d made it yourself because it will be full of dependencies you don’t understand. The second is the bloat — a “hello world” create-react-app application downloads over 150MB of JavaScript modules into your source tree and takes over 10 seconds to build into a minified JavaScript bundle which weighs in at over 500KB. This means you won’t want to be doing a rebuild in your development cycle every time you’ve changed a line of code.

Fortunately, create-react-app includes the webpack development server. Just by running npm run start you’ll get a standalone web server running on port 3000 that serves your React components and also rebuilds automatically whenever you save a source file. However, this gives you a bit of a conundrum if you’re writing the server-side APIs as well: If you serve the react components from a different server to where your server APIs are, you’ll end up confronting all sorts of cross-site scripting issues, but if you want to serve them from the same server, you’ll have to integrate the webpack development server into your server-side development environment (in this case, a Clojure ring web server).

The ASP.NET version of my project calls a magical function provided by Microsoft called UsetReactDevelopmentServer(). The code is complicated to reverse-engineer completely but I could see that it works by spinning up a webpack dev server then proxying the appropriate requests into it, thus exposing the React assets through the same server as your server-side code. Although I couldn’t find anything like this available for Clojure (or even Java), I guessed it would be pretty easy to write a proxy server in Clojure. This is what I ended up with:

(ns cupid.core
(:gen-class)
(:require [clojure.core.incubator :refer [dissoc-in]]
[compojure.core :refer [defroutes GET ANY]]
[clj-http.client :as client]
[ring.util.response :refer [not-found]]))
(defn proxy-handler
[request]
(-> {:method (request :request-method)
:url (str "http://localhost:3000"
(:uri request) "?" (:query-string request))
:headers (-> (request :headers)
(assoc "host" "localhost:3000")
(dissoc "accept-encoding"))}
client/request
(dissoc :chunked?)
(dissoc-in [:headers "Transfer-Encoding"])))
(defroutes my-routes
(ANY "/api/*" [] api-handler)
(GET "/sockjs-node" [] (not-found "Not Found"))
proxy-handler)

In Clojure, functions are declared before they are used, so let’s start with the routes declared at the bottom. All of my API endpoints start with “/api”, so we route those first, and then anything else is assumed to be part of the React application so is handled by the proxy handler (I’ll explain sockjs-node in a minute).

The proxy handler takes the incoming request and constructs a new one with the same method, the base part of the URI swapped to http://localhost:3000, and mostly the same headers. Then it threads it through clj-http.client to send it to the webpack dev server, and tweaks the response a bit.

The reason for the tweaking is that by default it uses chunked transfer encoding, which is much more difficult to proxy. I found that by removing the “accept-encoding” from the request and the chunking information from the response, my browser was able to load the pages just fine.

Finally, the webpack dev server has a natty “hot reload” feature that, when you save a source file, not only rebuilds the React components, it automatically refreshes the browser to reload them. Unfortunately, this uses a web socket, which again is difficult to proxy, so I just catch the sockjs-node URI used to invoke it and return a 404 (if you leave that line out, you’ll get sporadic exceptions in your server logs every time the browser times out on trying to initialize it). It means that you’ll have to manually refresh the browser every time you make a change to the React code.

The functionality we have here is the minimum I found necessary to get my react app working from the clojure server. If you wanted to improve it a bit, the two most useful additions would be to automatically start the webpack dev server during your own server startup, and get the hot reload working.

In case it’s not obvious, you’ll need to spin up the webpack dev server separately in another terminal window. If you forget to do this, depending on how you handle errors in your server, you might find errors like “connection refused” appearing the browser. This caught me out a few times as I assumed it was my web server that wasn’t running whereas actually it was the webpack one.

So that’s how to get developing React compoents with Clojure server APIs. In the next blog, I cover how to build the app into a standalone jar, and then host it on Microsoft Azure.

--

--