In this guide, I describe many router libraries in Clojure and give my recommendations.
Routing simply means deciding what to do based on the path of the web request. Every web request has a path (even if it’s just
/). When you get the request, you have to decide what to do with it. Routers take the path, which is just a string, and try to match it to one of the defined routes. Some routers will return a handler function, some call the handler function directly, and some return a keyword or other name to indicate what route was matched.
1. Does it work in Clojure and ClojureScript?
If you’re doing both frontend and backend, you want to be able to share the routes between them. Having the same code work on the server and the client might be useful.
2. How are routes defined?
Some libraries use macros, some use functions, and some use data.
3. What format are the routes defined in?
Some libraries let you use strings to define the routes, some with nice syntax for defining variable sections in the path. Some want you to break up your routes into a tree structure. Some let you define a regex or function for parsing variable sections. You can parse digits in the path into integers, for instance.
4. Is it bidirectional?
Of course, the main operation of the router is to take a path and decide what to do. However, to stay DRY, you can use the same route definition to go in the opposite direction: given the name of the route, what path does that correspond to? You can use that to generate links in your HTML by name. That way, you will be sure to get the links right, even if the path itself changes.
5. Does it do more than routing?
Sometimes libraries integrate other related functionality. For example, instead of just routing based on the path, it also uses the HTTP Request Method (GET, PUT, POST, DELETE, etc). While this extra functionality is often convenient, it’s not always compliant with the HTTP spec. This sometimes matters, but very often it doesn’t.
Before I get into the router libraries, I want to emphasize that no router could be perfect for every situation. There are some routers that I wouldn’t recommend because they’re not widely used and there are better options. And there are some that are just fine. And there are some that I think are best-in-class for that particular use case. I’m going to call those out.
These are my two top picks for routers as of September 2019. I had to pick two because both of them are good for different things. Reitit is great for a high-performance, industrial-strength, full-control router—the kind of thing you could build a company on. Compojure is great, too, but it’s more for convenience than anything else. If you want something for a side project that you don’t think will get much traffic, Compojure will suit you well.
Reitit is my recommendation for an industrial-strength, fast, and versatile router. It comes from the folks at Metosin, who have built great web libraries. Reitit is their router, and it is fast. Metosin is doing optimization and benchmarking to make a router that is highly competitive with other language alternatives, yet still compatible with the Clojure ecosystem.
Reitit checks a lot of boxes: it works in Clojure and ClojureScript, it’s data-driven, it’s bi-directional, and it sticks to routing. The documentation is great, too.
It has helpers for turning the routes into a Ring handler and an Interceptor, though you don’t need to use those. There are basic routing operations that don’t require Ring or Interceptors.
And what’s most impressive is that they achieve all of that while being the fastest Clojure router out there. There’s more about routing performance on this page.
Compojure has been out there for a long time and it is still widely used. It’s my recommendation for a “just get something working” routing library. I wouldn’t use it on serious projects anymore, but for quick side projects that don’t need to be super fast and won’t get too big, I think it still has a lot going for it.
Compojure has some strikes against it, in my book. The two main things it does wrong are using macros to define routes and to dispatch on the HTTP Method before dispatching on the path (though you can avoid it if you want to; it’s just extra work). However, if you look at these two strikes as features, they come together into a package that makes it much easier to get started. That’s why I recommend it for getting a web site/application up quickly. I use it for single-file web applications, despite my qualms.
Compojure doesn’t work in ClojureScript and it isn’t bi-directional. It’s intended for use with Ring, since it outputs a Ring handler.
It also does other stuff besides routing: it “intelligently” detects the type of response and does the right thing with it. For instance, if you return a string, it will assume that you meant to respond 200 OK, and the string is the body of the response. If you return a map, it considers that a Ring response. And if you output a function, it considers it a Ring handler and calls it for you. It also defines custom “routes” for not found routes and for static files. All of these things are really outside of the realm of routing, yet they are super convenient.
These libraries are high-quality and production-ready. They do what you need and do it well.
bidi is from the fine folks at JUXT. It’s a data-driven, bi-directional, Clojure+ClojureScript router. It works well, but Reitit does everything it does while being faster.
Pedestal is a popular library for building web applications. There are a lot of pieces of functionality that you can use separately. One of those pieces is a router. A lot of people use it and love it. It’s very fast. It’s bi-directional and data-driven.
Ataraxy comes at you from Weavejester, the programmer behind Compojure and current maintainer of Ring. Ataraxy is data-driven and bi-directional. It’s different because in addition to choosing what to do, it lets you destructure the Ring request to bundle everything you need to handle it in one place. The end result is it can turn Ring requests directly into compact domain-level actions in a declarative way.
Calfpath is another router. It’s data-driven, but it also has macros for convenience. It operates on Ring requests, not path strings, so you’ll hook it up directly to the handler. It’s quite fast and runs in a high-volume production environment. The routes look a bit complicated, but it’s probably just that it’s exposing all of the control to the programmer.
These libraries probably work. But for one reason or another, I wouldn’t use them in a new project. They’re either old, missing key features, or are completely superseded by better solutions. These libraries are in no particular order.
Clout is a route matching library. One feature that makes it an outlier is that it operates on full Ring requests, not on path strings. It’s not a dealbreaker, because most things use Ring requests. It also does not seem to have a way to combine multiple routes into a dispatcher. Instead, routes either match (returning a hashmap of parameters) or return nil.
Secretary was an early ClojureScript-only router. Stick with one of the Clojure+ClojureScript ones instead of this one.
bide is a ClojureScript router that is data-driven and bi-directional. It has such a clear syntax, it’s worth checking out if that’s for you.
Sibiro is Clojure+ClojureScript, data-driven, and bi-directional.
Moustache is an intricate DSL for wiring routes to handlers. It’s quite old.
Route One is bi-directional, but it doesn’t look like it works in ClojureScript. It uses macros to define the routes.
Trout is a ClojureScript-only, data-driven router.
gudu is bi-directional and data-driven.
Silk is Clojure+ClojureScript, bi-directional, and data-driven.
clj-routing was the original bi-directional routing library for Clojure. Its last commit predates ClojureScript. It was early to the game and paved the way for the other libraries.
A Clojure routing library. It uses macros to define global routes. An interesting feature is that it can generate entire Ring requests from the route name.