How does serving html, css, and javascript fit in a clojure web app?

When developing a clojure web app there are often static pieces that need to be served. Javascript and CSS are one example. Static html, such as a security or about page, is another. The java ecosystem's term for these are resources. By using resource functions they can be packaged and deployed in a jar file.

Finding pre-packaged resources

There are a couple projects that maintain packaged versions of javascript and css libraries. If you are using clojurescript, then cljsjs will be useful for compatible versions. If you are just serving javascript/css, many libraries have been packaged on webjars.org.

Packaging your own resources

If the library you want isn't packaged in the projects above, or is specific you your site, you can put into your project. In a standard leiningen project resources go in resources/. Many sites use resources/public for files that will be served as-is.

Serving resources

If you want to handle routing, ring.util.response/resource-response will return a ring response for the resource. The first argument is the path of the resource. The second argument is a map of options, the most common one being :root. This lets you preface the path with a parent path to start from.

(defroutes app-routes
  (GET "/" [] (resp/resource-response "index.html" {:root "public"})))

If your resource paths line up with your url paths, then ring.middleware.resource/wrap-resource may be easier. This middleware will look at all requests and see if a resource exists with that path. If one does then it returns a response with that resource, otherwise it allows normal routing to occur. The second argument to wrap-resource is the directory to preface with. This is useful so that not everything in the resource path is available.

(def app
  (-> routes
      (wrap-resource "public")))

There are some additional middleware that can be useful such as wrap-content-type and wrap-no-modified. The ring wiki has more information on these.

Debugging resources

Sometimes you might think the resource is there, but it just isn't working. Unfortunately the jvm makes it near impossible to list available resources. However, clojure.java.io/resource is useful to see if the resource exists where you are expecting. It will return a URL if the resource exists, and nil if it doesn't.

clojars.main> (clojure.java.io/resource "favicon.ico")
nil
clojars.main> (clojure.java.io/resource "public/favicon.ico")
#<URL file:/home/xeqi/workspace/tmp/clojars-web/resources/public/favicon.ico>

In this example, the resource public/favicon.ico exists. To have that mapped to the url "/favicon.ico" just requires (wrap-resource handler "public") in the middleware stack. If the routing is different then (resp/resource-response "favicon.ico" {:root "public"}) could be used as the ring response from the ring handler.