Skip to main content

Router

Routes are just data and to do routing, we need a router instance satisfying the reitit.core/Router protocol. Routers are created with reitit.core/router function, taking the raw routes and optionally an options map.

The Router protocol:

(defprotocol Router
(router-name [this])
(routes [this])
(options [this])
(route-names [this])
(match-by-path [this path])
(match-by-name [this name] [this name params]))

Creating a router:

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

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

Name of the created router:

(r/router-name router)
; :mixed-router

The flattened route tree:

(r/routes router)
; [["/api/ping" {:name :user/ping}]
; ["/api/user/:id" {:name :user/user}]]

With a router instance, we can do Path-based routing or Name-based (Reverse) routing.

More details

Router options:

(r/options router)
{:lookup #object[...]
:expand #object[...]
:coerce #object[...]
:compile #object[...]
:conflicts #object[...]}

Route names:

(r/route-names router)
; [:user/ping :user/user]

Composing

As routes are defined as plain data, it's easy to merge multiple route trees into a single router

(def user-routes
[["/users" ::users]
["/users/:id" ::user]])

(def admin-routes
["/admin"
["/ping" ::ping]
["/db" ::db]])

(def router
(r/router
[admin-routes
user-routes]))

Merged route tree:

(r/routes router)
; [["/admin/ping" {:name :user/ping}]
; ["/admin/db" {:name :user/db}]
; ["/users" {:name :user/users}]
; ["/users/:id" {:name :user/user}]]

More details on composing routers.

Behind the scenes

When router is created, the following steps are done:

  • route tree is flattened
  • route arguments are expanded (via :expand option)
  • routes are coerced (via :coerce options)
  • route tree is compiled (via :compile options)
  • route conflicts are resolved (via :conflicts options)
  • optionally, route data is validated (via :validate options)
  • router implementation is automatically selected (or forced via :router options) and created