How do you make a web application in Clojure? [closed]

I suppose this is a strange question to the huge majority of programmers that work daily with Java. I don't. I know Java-the-language, because I worked on Java projects, but not Java-the-world. I never made a web app from scratch in Java. If I have to do it with Python, Ruby, I know where to go (Django or Rails), but if I want to make a web application in Clojure, not because I'm forced to live in a Java world, but because I like the language and I want to give it a try, what libraries and frameworks should I use?


Solution 1:

Compojure is no longer a complete framework for developing web applications. Since the 0.4 release, compojure has been broken off into several projects.

Ring provides the foundation by abstracting away the HTTP request and response process. Ring will parse the incoming request and generate a map containing all of the parts of the request such as uri, server-name and request-method. The application will then handle the request and based on the request generate a response. A response is represented as a map containing the following keys: status, headers, and body. So a simple application would look like:

(def app [req]
  (if (= "/home" (:uri req))
    {:status 200
     :body "<h3>Welcome Home</h3>"}
    {:status 200 
     :body "<a href='/home'>Go Home!</a>"}))

One other part of Ring is the concept of middle-ware. This is code that sits between the handler and the incoming request and/or the outgoing response. Some built in middle-ware include sessions and stacktrace. The session middle-ware will add a :session key to the request map that contains all of the session info for the user making the request. If the :session key is present in the response map, it will be stored for the next request made by the current user. While the stack trace middle-ware will capture any exceptions that occur while processing the request and generate a stack trace that is sent back as the response if any exceptions do occur.

Working directly with Ring can be tedious, so Compojure is built on top of Ring abstracting away the details. The application can now be expressed in terms of routing so you can have something like this:

(defroutes my-routes
  (GET "/" [] "<h1>Hello all!</h1>")
  (GET "/user/:id" [id] (str "<h1>Hello " id "</h1>")))

Compojure is still working with the request/response maps so you can always access them if needed:

(defroutes my-routes
  (GET "*" {uri :uri} 
           {:staus 200 :body (str "The uri of the current page is: " uri)}))

In this case the {uri :uri} part accesses the :uri key in the request map and sets uri to that value.

The last component is Hiccup which makes generating the html easier. The various html tags are represented as vectors with the first element representing the tag name and the rest being the body of the tag. "<h2>A header</h2>" becomes [:h2 "A Header"]. The attributes of a tag are in an optional map. "<a href='/login'>Log In Page</a>" becomes [:a {:href "/login"} "Log In Page"]. Here is a small example using a template to generate the html.

(defn layout [title & body]
  (html
    [:head [:title title]]
    [:body [:h1.header title] body])) 

(defn say-hello [name]
  (layout "Welcome Page" [:h3 (str "Hello " name)]))

(defn hiccup-routes
  (GET "/user/:name" [name] (say-hello name)))

Here is a link to a rough draft of some documentation currently being written by the author of compojure that you might find helpful: Compojure Doc

Solution 2:

By far the best Clojure web framework I have yet encountered is Compojure: http://github.com/weavejester/compojure/tree/master

It's small but powerful, and has beautifully elegant syntax. (It uses Jetty under the hood, but it hides the Servlet API from you unless you want it, which won't be often). Go look at the README at that URL, then download a snapshot and start playing.

Solution 3:

There's also "Noir" (http://www.webnoir.org/), which is a new Clojure web framework (so new the docs aren't there yet). Coming from Django/Rails, I dig the simple, straightforward syntax and it's pretty lean.

Solution 4:

Consider the Luminus web framework. I have no affiliation but have heard good things from friends I respect.

Solution 5:

My current go-to web library is now yada.

If you are just starting out, the introductory server is Compojure. I see it as the apache of web servers in the Clojure world (in which case yada/aleph would be nginx). You could use Luminus as a template. There are variants of it, like compojure-api.

I tried ou Pedestal and was globally satisfied with it. I don't claim to master it, but it has a pleasant syntax, feels very cohesive, and looks like it does have great performance. It is also backed by Cognitect (the Clojure/Datomic company where Rich Hickey works).

I found Aleph to present an interesting abstraction, and the built-in backpressure seems interesting. I have yet to play with it, but it's definitely on my list.

After playing a bit with various web servers, here is my quick Pro/Cons list :

Short answer : have a look at Luminus to get started quickly, maybe move on to something else as your needs evolve (Yada maybe).

Compojure

  • Pros (1):

    • easy, lots of templates/examples (ex. Luminous)
  • Cons (2):

    • Not performant (a thread per request), expect performances slightly better than rails
    • Not simple, the middleware model has inconvenients

Pedestal

  • Pros (3):

    • interceptor model, pleasant syntax to add interceptors to a subset of routes
    • performant router
    • supports json/transit/multipart forms transparently out of the box, without asking anything. Very cool !
  • Cons (4):

    • no websocket support (yet), returning core.async channels would be nice
    • a bit slow to reload if putting it in a Stuart Sierra's component (I think you are supposed to use the reload interceptor)
    • no testing facility for async interceptors
    • requires buy-in (?)

Aleph

Pro (3):

  • Performant
  • backpressure
  • Websocket/SSE support when returning a manifold stream

Cons (1):

  • Low level, do it yourself style (ie. it just gives you a way to make your handlers do something. No router, no nothing). Not really a cons, just be aware of it.

Yada

Pro (3):

  • built on Aleph
  • content negociation
  • swagger integration
  • bidi is quite ok (though I like pedestal router syntax better)

Cons (1):

  • documentation (although not as bad as nginx-clojure, quickly improving).

HttpKit

Pro (2):

  • Written in Clojure ! (and Java...)
  • performance looks good (see the 600K concurrent connections post)

Cons (2):

  • No CORS support
  • Bugs ? Also, not a lot of recent commits

Nginx-Clojure

Note : I haven't played with it, mainly because of the lack of documentation. It looks interesting though, and very performant.

Pros (2):

  • Nginx (performant, offload ssl, restart workers...)
  • Could this model allow zero-downtime updates ? That would be so awesome !

Cons (1):

  • Documentation (improving). Also, I don't want to program in strings embedded in an nginx config file if that is the only way to do it.
  • Probably complicates a bit the first deployment (?)

Immutant

Note : I haven't played with it.

Pros :

  • integrated (caching, messaging, scheduling, wildfly deploy)

Cons :

  • no http client

Catacumba

Note : I haven't played with it, although the documentation looks excellent. I am probably going to try it next. There are example chat projects that look interesting, their heavy use of protocols put me off at first as a novice Clojure dev.

Pros (6):

  • documentation ! Like all funcool projects, the doc is very pleasant to read.
  • pedestal-like routing syntax
  • should be performant (on top of Ratpack)
  • backpressure
  • websockets, sse, cors, security, ssl...
  • unique features to dig : postal

Cons (2):

  • Not completely sure about how pleasant the ct/routes syntax is, and about ditching the Ring spec (supposedly for the async story, but I thought the pedestal guys fixed that)
  • Not sure how one would integrate swagger etc.
  • when I tried it, I was not able to make it work straight away

Note : a benchmark of Clojure web servers is available, if raw performance is all that matters.