Library / SDK
metosin/reitit avatar
metosin/reitit

reitit routing for Clojure and ClojureScript

A fast data-driven routing library for Clojure/Script

1,587 stars265 forksClojureEPL-1.0

At a glance

What is it?
reitit turns ordinary Clojure data into a router for path and name matching, with separate modules for Ring, HTTP interceptors, coercion, frontend routing, and API documentation. It fits Clojure applications that want routing rules to remain inspectable data instead of being encoded as procedures.
Who is it for?
Clojure and ClojureScript teams that want routing definitions and generated route data in ordinary data structures should evaluate reitit. Projects that need no coercion, middleware, or API documentation should compare the cost of its module system with a simpler router.
Can I use it commercially?
Yes, with conditions. EPL-1.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly Clojure, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • metosin/reitit1,587 ★ · +0
Star history
Repository2026-10-162026-10-23
metosin/reitit1.6k1.6k

0 Stars in the last 7 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

reitit's data-driven routing model

reitit solves route lookup and URL generation for Clojure and ClojureScript applications. Its route syntax is plain data, normally a vector whose first item is a path template and whose second item identifies a route. A Clojure keyword such as `::ping` can serve as that identifier, so lookup does not depend on naming an arbitrary function before the router exists. This separation makes the router useful both at runtime and when another part of an application needs to inspect the route catalog.

The model covers several distinct jobs. Path matching maps a request path to route data. Name matching goes in the opposite direction and creates a path from a route name and supplied parameters. Reitit can also merge additional data into each route, expose that data as the `:data` key in a match, report conflicting definitions, and provide helpers for API documentation. The README calls these first-class route data, bi-directional routing, and pluggable coercion, but the practical point is that one route definition can support more than URL dispatch.

The intended audience is not limited to HTTP servers. ClojureScript projects can use `reitit-frontend`, while server applications can select Ring, HTTP interceptors, Pedestal, or smaller functional modules. This makes reitit relevant to full-stack teams that want a shared routing vocabulary. The trade-off is a larger set of concepts than a project with only a handful of static paths needs.

Path matching and name-based URL generation

The quickest documented example creates one router and exercises both directions of routing. `match-by-path` recognizes `/api/ping`, while `match-by-name` constructs `/api/orders/2` from `::order` and `{:id 2}`. A match carries `:template`, `:data`, `:result`, `:path-params`, and `:path`. In the static case, the path parameters remain empty. In the parameterized case, `2` appears both as the supplied input and as the rendered path parameter.

That example exposes an important API convention. Route identity is the keyword stored beside the template, not merely the HTTP path. Developers should therefore choose stable, namespaced keywords if application code depends on named lookup. The README does not document rollback behavior or persistence for route definitions, so this should be treated as an in-memory application structure rather than a database-backed route manager.

Reitit also provides a conflict-resolution layer for its route syntax. The README links dedicated documentation for this mechanism but does not show resolution outcomes in the quick start. That omission matters when route trees are assembled from multiple modules. Before adopting a hierarchy, check how the selected syntax treats overlapping static and parameterized paths in the target reitit version.

Installing reitit and running the first route match

The README lists the bundled dependency as `[metosin/reitit "0.11.0"]` and requires Clojure 1.11 with Java 11. It states that reitit is tested with Java 11, 17, 21, and 25. Add the dependency through the build tool already used by the application; the README does not prescribe a command for `lein`, `deps.edn`, or another tool.

clojure
[metosin/reitit "0.11.0"]

After the dependency is resolved, this namespace loads the core API and defines a router with one static route and one parameterized route:

clojure
(require '[reitit.core :as r])

(def router
  (r/router
    [["/api/ping" ::ping]
     ["/api/orders/:id" ::order]]))

The first real check matches an exact path. It should return a `#Match` whose template is `/api/ping` and whose data contains the name `::ping`.

clojure
(r/match-by-path router "/api/ping")

The reverse direction should return a `#Match` with `:path "/api/orders/2"` and `:path-params {:id 2}`:

clojure
(r/match-by-name router ::order {:id 2})

These calls verify routing without introducing an HTTP server, serialization format, or authentication layer.

Ring coercion and middleware composition

For a Ring application, `reitit-ring` wraps route data in a Ring handler. The documented example adds `clojure.spec` coercion, Muuntaja formatting, request coercion, response coercion, parameter decoding, and exception handling. Middleware is attached through router data, so the same route declaration can describe an HTTP method, accepted query parameters, response schema, handler, and the middleware needed to process it.

The handler for `GET /api/math` declares query members `x` and `y` as integers and returns a map with `:status 200` and `{:total (+ x y)}`. Since query values arrive as strings in the example request, request coercion is what connects the Ring input to the handler's declared integer values. The Muuntaja middleware then formats the response as JSON.

clojure
{:get {:parameters {:query {:x int?, :y int?}}
       :responses  {200 {:body {:total int?}}}
       :handler    (fn [{{{:keys [x y]} :query} :parameters}]
                     {:status 200
                      :body   {:total (+ x y)}})}}

This composition is explicit, but it also expands the dependency surface. The example requires `muuntaja.core`, `reitit.ring`, `reitit.coercion.spec`, coercion middleware namespaces, exception middleware, Muuntaja middleware, and parameter middleware. A team should include only the integration and coercion modules that its request contract requires.

Module boundaries and the HTTP interceptor alternative

Reitit is modular rather than being only the all-in-one Ring application. `metosin/reitit-core` contains routing core, while separate artifacts cover Ring, common Ring middleware, `clojure.spec`, Malli, Schema, HTTP interceptors, frontend routing, Swagger 2, and OpenAPI. The bundled `metosin/reitit` artifact includes all main modules. This allows a backend-only service to avoid selecting every adapter, while a full-stack project can adopt `reitit-frontend` and `reitit-http` independently.

Ring is one way to handle requests. The `reitit-http` module uses interceptors instead, and `reitit-pedestal` supplies a Pedestal integration. These are real differences in execution shape, not just naming variants. The Ring example represents each route with middleware and a handler that returns a Ring response map. The interceptor approach is centered on the interceptor model documented for `reitit-http`. Pedestal applications can instead use the dedicated integration module.

For non-HTTP use, the core router remains the narrower option. It performs matching and URL generation without requiring request formatting, coercion, or a web-server abstraction. That makes core-only use the relevant alternative when a ClojureScript or server-side component needs route identity and path construction, but does not handle HTTP requests.

reitit maintenance, licensing, and adoption limits

The repository describes reitit as stable and was last pushed on 2026-10-07. Recent releases include `0.11.0` on 2026-09-18, `0.11.0-rc1` on 2026-08-07, and `0.10.1` on 2026-03-10. These dates support recent project activity, but they do not establish API compatibility between releases. The README does not provide a migration or rollback procedure for upgrading a router tree, so an adoption should inspect the changelog for the specific version transition.

The repository lists `EPL-1.0` as its license. Projects must account for that license and the licenses of separately used adapters or coercion libraries when assembling dependencies. This is a repository and packaging fact, not advice on legal treatment.

The clearest wrong-fit case is an application that needs only a fixed list of ordinary Clojure functions to map paths to handlers. Reitit's route data, conflict handling, coercion, documentation, and integration modules would then exceed the requirement. Its own README does not claim to cover deployment, persistence, or rollback, and it does not document a general-purpose service framework. For Reitit users, the concrete first check is whether Clojure 1.11 and Java 11 match the runtime before examining the optional modules.

Editorial conclusion

Clojure and ClojureScript teams that want routing definitions and generated route data in ordinary data structures should evaluate reitit. Projects that need no coercion, middleware, or API documentation should compare the cost of its module system with a simpler router. The first adoption checks are Java and Clojure compatibility, route conflict behavior in the application's chosen syntax, and whether the required integration belongs in the Ring or HTTP interceptor pipeline.

Frequently asked questions

How do I install reitit for Clojure?

Add `[metosin/reitit "0.11.0"]` through the application's Clojure build tool. Reitit requires Clojure 1.11 and Java 11, and the README states that it is tested with Java 11, 17, 21, and 25.

Can reitit generate URLs from route names?

Yes. `reitit.core/match-by-name` accepts a router, a route keyword, and route parameters; the documented example turns `::order` and `{:id 2}` into `/api/orders/2`.

Does reitit support Ring applications and request coercion?

Yes. `reitit-ring` provides the Ring router, while coercion and middleware namespaces can validate and transform requests and format responses. The README demonstrates `clojure.spec` query coercion with a Ring handler.

Official sources

  1. License: EPL-1.0
  2. metosin/reitit on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/metosin-reitit.svg)](https://hysenlabs.com/projects/metosin-reitit)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/metosin-reitit.svg)](https://hysenlabs.com/projects/metosin-reitit)

Was this useful?