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.
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.
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.
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.