2018-07-18 10:13:28 +00:00
<!DOCTYPE HTML>
< html lang = "" >
< head >
< meta charset = "UTF-8" >
< meta content = "text/html; charset=utf-8" http-equiv = "Content-Type" >
< title > Composing Routers · GitBook< / title >
< meta http-equiv = "X-UA-Compatible" content = "IE=edge" / >
< meta name = "description" content = "" >
< meta name = "generator" content = "GitBook 3.2.3" >
< link rel = "stylesheet" href = "../gitbook/style.css" >
< link rel = "stylesheet" href = "../gitbook/gitbook-plugin-highlight/website.css" >
< link rel = "stylesheet" href = "../gitbook/gitbook-plugin-search/search.css" >
< link rel = "stylesheet" href = "../gitbook/gitbook-plugin-fontsettings/website.css" >
< meta name = "HandheldFriendly" content = "true" / >
< meta name = "viewport" content = "width=device-width, initial-scale=1, user-scalable=no" >
< meta name = "apple-mobile-web-app-capable" content = "yes" >
< meta name = "apple-mobile-web-app-status-bar-style" content = "black" >
< link rel = "apple-touch-icon-precomposed" sizes = "152x152" href = "../gitbook/images/apple-touch-icon-precomposed-152.png" >
< link rel = "shortcut icon" href = "../gitbook/images/favicon.ico" type = "image/x-icon" >
< link rel = "next" href = "different_routers.html" / >
< link rel = "prev" href = "configuring_routers.html" / >
< / head >
< body >
< div class = "book" >
< div class = "book-summary" >
< div id = "book-search-input" role = "search" >
< input type = "text" placeholder = "Type to search" / >
< / div >
< nav role = "navigation" >
< ul class = "summary" >
< li class = "chapter " data-level = "1.1" data-path = "../" >
< a href = "../" >
Introduction
< / a >
< / li >
< li class = "chapter " data-level = "1.2" data-path = "../basics/" >
< a href = "../basics/" >
Basics
< / a >
< ul class = "articles" >
< li class = "chapter " data-level = "1.2.1" data-path = "../basics/route_syntax.html" >
< a href = "../basics/route_syntax.html" >
Route Syntax
< / a >
< / li >
< li class = "chapter " data-level = "1.2.2" data-path = "../basics/router.html" >
< a href = "../basics/router.html" >
Router
< / a >
< / li >
< li class = "chapter " data-level = "1.2.3" data-path = "../basics/path_based_routing.html" >
< a href = "../basics/path_based_routing.html" >
Path-based Routing
< / a >
< / li >
< li class = "chapter " data-level = "1.2.4" data-path = "../basics/name_based_routing.html" >
< a href = "../basics/name_based_routing.html" >
Name-based Routing
< / a >
< / li >
< li class = "chapter " data-level = "1.2.5" data-path = "../basics/route_data.html" >
< a href = "../basics/route_data.html" >
Route Data
< / a >
< / li >
< li class = "chapter " data-level = "1.2.6" data-path = "../basics/route_data_validation.html" >
< a href = "../basics/route_data_validation.html" >
Route Data Validation
< / a >
< / li >
< li class = "chapter " data-level = "1.2.7" data-path = "../basics/route_conflicts.html" >
< a href = "../basics/route_conflicts.html" >
Route Conflicts
< / a >
< / li >
< / ul >
< / li >
< li class = "chapter " data-level = "1.3" data-path = "../coercion/" >
< a href = "../coercion/" >
Coercion
< / a >
< ul class = "articles" >
< li class = "chapter " data-level = "1.3.1" data-path = "../coercion/coercion.html" >
< a href = "../coercion/coercion.html" >
Coercion Explained
< / a >
< / li >
< li class = "chapter " data-level = "1.3.2" data-path = "../coercion/schema_coercion.html" >
< a href = "../coercion/schema_coercion.html" >
Plumatic Schema
< / a >
< / li >
< li class = "chapter " data-level = "1.3.3" data-path = "../coercion/clojure_spec_coercion.html" >
< a href = "../coercion/clojure_spec_coercion.html" >
Clojure.spec
< / a >
< / li >
< li class = "chapter " data-level = "1.3.4" data-path = "../coercion/data_spec_coercion.html" >
< a href = "../coercion/data_spec_coercion.html" >
Data-specs
< / a >
< / li >
< / ul >
< / li >
< li class = "chapter " data-level = "1.4" data-path = "./" >
< a href = "./" >
Advanced
< / a >
< ul class = "articles" >
< li class = "chapter " data-level = "1.4.1" data-path = "configuring_routers.html" >
< a href = "configuring_routers.html" >
Configuring Routers
< / a >
< / li >
< li class = "chapter active" data-level = "1.4.2" data-path = "composing_routers.html" >
< a href = "composing_routers.html" >
Composing Routers
< / a >
< / li >
< li class = "chapter " data-level = "1.4.3" data-path = "different_routers.html" >
< a href = "different_routers.html" >
Different Routers
< / a >
< / li >
< li class = "chapter " data-level = "1.4.4" data-path = "route_validation.html" >
< a href = "route_validation.html" >
Route Validation
< / a >
< / li >
< li class = "chapter " data-level = "1.4.5" data-path = "dev_workflow.html" >
< a href = "dev_workflow.html" >
Dev Workflow
< / a >
< / li >
< / ul >
< / li >
< li class = "chapter " data-level = "1.5" data-path = "../ring/" >
< a href = "../ring/" >
Ring
< / a >
< ul class = "articles" >
< li class = "chapter " data-level = "1.5.1" data-path = "../ring/ring.html" >
< a href = "../ring/ring.html" >
Ring-router
< / a >
< / li >
< li class = "chapter " data-level = "1.5.2" data-path = "../ring/reverse_routing.html" >
< a href = "../ring/reverse_routing.html" >
Reverse-routing
< / a >
< / li >
< li class = "chapter " data-level = "1.5.3" data-path = "../ring/default_handler.html" >
< a href = "../ring/default_handler.html" >
Default handler
< / a >
< / li >
< li class = "chapter " data-level = "1.5.4" data-path = "../ring/static.html" >
< a href = "../ring/static.html" >
Static Resources
< / a >
< / li >
< li class = "chapter " data-level = "1.5.5" data-path = "../ring/dynamic_extensions.html" >
< a href = "../ring/dynamic_extensions.html" >
Dynamic Extensions
< / a >
< / li >
< li class = "chapter " data-level = "1.5.6" data-path = "../ring/data_driven_middleware.html" >
< a href = "../ring/data_driven_middleware.html" >
Data-driven Middleware
< / a >
< / li >
< li class = "chapter " data-level = "1.5.7" data-path = "../ring/coercion.html" >
< a href = "../ring/coercion.html" >
Pluggable Coercion
< / a >
< / li >
< li class = "chapter " data-level = "1.5.8" data-path = "../ring/route_data_validation.html" >
< a href = "../ring/route_data_validation.html" >
Route Data Validation
< / a >
< / li >
< li class = "chapter " data-level = "1.5.9" data-path = "../ring/compiling_middleware.html" >
< a href = "../ring/compiling_middleware.html" >
Compiling Middleware
< / a >
< / li >
< li class = "chapter " data-level = "1.5.10" data-path = "../ring/swagger.html" >
< a href = "../ring/swagger.html" >
Swagger Support
< / a >
< / li >
< / ul >
< / li >
< li class = "chapter " data-level = "1.6" data-path = "../frontend/" >
< a href = "../frontend/" >
Frontend
< / a >
< ul class = "articles" >
< li class = "chapter " data-level = "1.6.1" data-path = "../frontend/basics.html" >
< a href = "../frontend/basics.html" >
Basics
< / a >
< / li >
< li class = "chapter " data-level = "1.6.2" data-path = "../frontend/browser.html" >
< a href = "../frontend/browser.html" >
Browser integration
< / a >
< / li >
< li class = "chapter " data-level = "1.6.3" data-path = "../frontend/controllers.html" >
< a href = "../frontend/controllers.html" >
Controllers
< / a >
< / li >
< / ul >
< / li >
< li class = "chapter " data-level = "1.7" data-path = "../performance.html" >
< a href = "../performance.html" >
Performance
< / a >
< / li >
< li class = "chapter " data-level = "1.8" data-path = "../interceptors.html" >
< a href = "../interceptors.html" >
Interceptors (WIP)
< / a >
< / li >
< li class = "chapter " data-level = "1.9" data-path = "../development.html" >
< a href = "../development.html" >
Development Instructions
< / a >
< / li >
< li class = "chapter " data-level = "1.10" data-path = "../faq.html" >
< a href = "../faq.html" >
FAQ
< / a >
< / li >
< li class = "divider" > < / li >
< li >
< a href = "https://www.gitbook.com" target = "blank" class = "gitbook-link" >
Published with GitBook
< / a >
< / li >
< / ul >
< / nav >
< / div >
< div class = "book-body" >
< div class = "body-inner" >
< div class = "book-header" role = "navigation" >
<!-- Title -->
< h1 >
< i class = "fa fa-circle-o-notch fa-spin" > < / i >
< a href = ".." > Composing Routers< / a >
< / h1 >
< / div >
< div class = "page-wrapper" tabindex = "-1" role = "main" >
< div class = "page-inner" >
< div id = "book-search-results" >
< div class = "search-noresults" >
< section class = "normal markdown-section" >
< h1 id = "composing-routers" > Composing Routers< / h1 >
2018-07-25 07:56:39 +00:00
< p > Once a router is created, the routing tree is immutable and cannot be changed. To modify the routes, we have to make a new copy of the router, with modified routes and/or options. For this, the < code > Router< / code > exposes the resolved routes via < code > r/routes< / code > and options via < code > r/options< / code > .< / p >
< h2 id = "adding-routes" > Adding routes< / h2 >
< p > Let' s create a router:< / p >
2018-07-18 10:13:28 +00:00
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > require< / span > ' [reitit.core < span class = "hljs-symbol" > :as< / span > r])
2018-07-25 07:56:39 +00:00
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > def< / span > < / span > router
(< span class = "hljs-name" > r/router< / span >
[[< span class = "hljs-string" > " /foo" < / span > < span class = "hljs-symbol" > ::foo< / span > ]
[< span class = "hljs-string" > " /bar/:id" < / span > < span class = "hljs-symbol" > ::bar< / span > ]]))
2018-07-18 10:13:28 +00:00
< / code > < / pre >
2018-07-25 07:56:39 +00:00
< p > It' s resolved routes and options:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > r/routes< / span > router)
< span class = "hljs-comment" > ;[[" /foo" {:name :user/foo}]< / span >
< span class = "hljs-comment" > ; [" /bar/:idr" {:name :user/bar}]]< / span >
(< span class = "hljs-name" > r/options< / span > router)
< span class = "hljs-comment" > ;{:lookup #object[...]< / span >
< span class = "hljs-comment" > ; :expand #object[...]< / span >
< span class = "hljs-comment" > ; :coerce #object[...]< / span >
< span class = "hljs-comment" > ; :compile #object[...]< / span >
< span class = "hljs-comment" > ; :conflicts #object[...]}< / span >
< / code > < / pre >
< p > A helper to create a new router with extra routes:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > defn< / span > < / span > add-routes [router routes]
2018-07-18 10:13:28 +00:00
(< span class = "hljs-name" > r/router< / span >
2018-07-25 07:56:39 +00:00
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > into< / span > < / span > (< span class = "hljs-name" > r/routes< / span > router) routes)
2018-07-18 10:13:28 +00:00
(< span class = "hljs-name" > r/options< / span > router)))
< / code > < / pre >
2018-07-25 07:56:39 +00:00
< p > New router with an extra route:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > def< / span > < / span > router2
(< span class = "hljs-name" > add-routes< / span >
router
[[< span class = "hljs-string" > " /baz/:id/:subid" < / span > < span class = "hljs-symbol" > ::baz< / span > ]]))
(< span class = "hljs-name" > r/routes< / span > router2)
< span class = "hljs-comment" > ;[[" /foo" {:name :user/bar}]< / span >
< span class = "hljs-comment" > ; [" /bar/:id" {:name :user/bar}]< / span >
< span class = "hljs-comment" > ; [" /baz/:id/:subid" {:name :user/baz}]]< / span >
2018-07-18 10:13:28 +00:00
< / code > < / pre >
2018-07-25 07:56:39 +00:00
< p > All rules are applied, including the conflict resolution:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > add-routes< / span >
router2
[[< span class = "hljs-string" > " /:this/should/:fail" < / span > < span class = "hljs-symbol" > ::fail< / span > ]])
< span class = "hljs-comment" > ;CompilerException clojure.lang.ExceptionInfo: Router contains conflicting route paths:< / span >
2018-07-18 10:13:28 +00:00
< span class = "hljs-comment" > ;< / span >
2018-07-25 07:56:39 +00:00
< span class = "hljs-comment" > ; /baz/:id/:subid< / span >
< span class = "hljs-comment" > ;-> /:this/should/:fail< / span >
2018-07-18 10:13:28 +00:00
< / code > < / pre >
< h2 id = "merging-routers" > Merging routers< / h2 >
2018-07-25 07:56:39 +00:00
< p > A helper to merge routers:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > defn< / span > < / span > merge-routers [& routers]
(< span class = "hljs-name" > r/router< / span >
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > apply< / span > < / span > merge (< span class = "hljs-name" > < span class = "hljs-builtin-name" > map< / span > < / span > r/routes routers))
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > apply< / span > < / span > merge (< span class = "hljs-name" > < span class = "hljs-builtin-name" > map< / span > < / span > r/options routers))))
< / code > < / pre >
< p > Merging three routers into one:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > def< / span > < / span > router
(< span class = "hljs-name" > merge-routers< / span >
(< span class = "hljs-name" > r/router< / span > [< span class = "hljs-string" > " /route1" < / span > < span class = "hljs-symbol" > ::route1< / span > ])
(< span class = "hljs-name" > r/router< / span > [< span class = "hljs-string" > " /route2" < / span > < span class = "hljs-symbol" > ::route2< / span > ])
(< span class = "hljs-name" > r/router< / span > [< span class = "hljs-string" > " /route3" < / span > < span class = "hljs-symbol" > ::route3< / span > ])))
(< span class = "hljs-name" > r/routes< / span > router)
< span class = "hljs-comment" > ;[[" /route1" {:name :user/route1}]< / span >
< span class = "hljs-comment" > ; [" /route2" {:name :user/route2}]< / span >
< span class = "hljs-comment" > ; [" /route3" {:name :user/route3}]]< / span >
< / code > < / pre >
< h2 id = "nesting-routers" > Nesting routers< / h2 >
< p > Routers can be nested too, using the catch-all parameter.< / p >
< p > A router with nested routers using a custom < code > :router< / code > key:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > def< / span > < / span > router
(< span class = "hljs-name" > r/router< / span >
[[< span class = "hljs-string" > " /ping" < / span > < span class = "hljs-symbol" > :ping< / span > ]
[< span class = "hljs-string" > " /olipa/*" < / span > {< span class = "hljs-symbol" > :name< / span > < span class = "hljs-symbol" > :olipa< / span >
< span class = "hljs-symbol" > :router< / span > (< span class = "hljs-name" > r/router< / span >
[[< span class = "hljs-string" > " /olut" < / span > < span class = "hljs-symbol" > :olut< / span > ]
[< span class = "hljs-string" > " /makkara" < / span > < span class = "hljs-symbol" > :makkara< / span > ]
[< span class = "hljs-string" > " /kerran/*" < / span > {< span class = "hljs-symbol" > :name< / span > < span class = "hljs-symbol" > :kerran< / span >
< span class = "hljs-symbol" > :router< / span > (< span class = "hljs-name" > r/router< / span >
[[< span class = "hljs-string" > " /avaruus" < / span > < span class = "hljs-symbol" > :avaruus< / span > ]
[< span class = "hljs-string" > " /ihminen" < / span > < span class = "hljs-symbol" > :ihminen< / span > ]])}]])}]]))
< / code > < / pre >
< p > Matching by path:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > r/match-by-path< / span > router < span class = "hljs-string" > " /olipa/kerran/iso/kala" < / span > )
< span class = "hljs-comment" > ;#Match{:template " /olipa/*" < / span >
< span class = "hljs-comment" > ; :data {:name :olipa< / span >
< span class = "hljs-comment" > ; :router #object[reitit.core$mixed_router]}< / span >
< span class = "hljs-comment" > ; :result nil< / span >
< span class = "hljs-comment" > ; :path-params {: " kerran/iso/kala" }< / span >
< span class = "hljs-comment" > ; :path " /olipa/iso/kala" }< / span >
< / code > < / pre >
< p > That not right, it should not have matched. The core routing doesn' t understand anything about nesting, so it only matched against the top-level router, which gave a match for the catch-all path. < / p >
< p > As the < code > Match< / code > contains the route data, we can create a new matching function that understands our custom < code > :router< / code > syntax. Here is a function that does recursive matching using the subrouters. It returns either < code > nil< / code > or a vector of mathces.< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > require< / span > ' [clojure.string < span class = "hljs-symbol" > :as< / span > str])
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > defn< / span > < / span > recursive-match-by-path [router path]
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > if-let< / span > < / span > [match (< span class = "hljs-name" > r/match-by-path< / span > router path)]
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > if-let< / span > < / span > [subrouter (< span class = "hljs-name" > < span class = "hljs-builtin-name" > -> < / span > < / span > match < span class = "hljs-symbol" > :data< / span > < span class = "hljs-symbol" > :router< / span > )]
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > if-let< / span > < / span > [submatch (< span class = "hljs-name" > recursive-match-by-path< / span > subrouter (< span class = "hljs-name" > subs< / span > path (< span class = "hljs-name" > str/last-index-of< / span > (< span class = "hljs-symbol" > :template< / span > match) < span class = "hljs-string" > " /" < / span > )))]
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > into< / span > < / span > [match] submatch))
[match])))
< / code > < / pre >
< p > With invalid nested path we get now < code > nil< / code > as expected:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > recursive-match-by-path< / span > router < span class = "hljs-string" > " /olipa/kerran/iso/kala" < / span > )
< span class = "hljs-comment" > ; nil< / span >
< / code > < / pre >
< p > With valid path we get all the nested matches:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > recursive-match-by-path< / span > router < span class = "hljs-string" > " /olipa/kerran/avaruus" < / span > )
< span class = "hljs-comment" > ;[#reitit.core.Match{:template " /olipa/*" < / span >
< span class = "hljs-comment" > ; :data {:name :olipa< / span >
< span class = "hljs-comment" > ; :router #object[reitit.core$mixed_router]}< / span >
< span class = "hljs-comment" > ; :result nil< / span >
< span class = "hljs-comment" > ; :path-params {: " kerran/avaruus" }< / span >
< span class = "hljs-comment" > ; :path " /olipa/kerran/avaruus" }< / span >
< span class = "hljs-comment" > ; #reitit.core.Match{:template " /kerran/*" < / span >
< span class = "hljs-comment" > ; :data {:name :kerran< / span >
< span class = "hljs-comment" > ; :router #object[reitit.core$lookup_router]}< / span >
< span class = "hljs-comment" > ; :result nil< / span >
< span class = "hljs-comment" > ; :path-params {: " avaruus" }< / span >
< span class = "hljs-comment" > ; :path " /kerran/avaruus" }< / span >
< span class = "hljs-comment" > ; #reitit.core.Match{:template " /avaruus" < / span >
< span class = "hljs-comment" > ; :data {:name :avaruus} < / span >
< span class = "hljs-comment" > ; :result nil < / span >
< span class = "hljs-comment" > ; :path-params {} < / span >
< span class = "hljs-comment" > ; :path " /avaruus" }]< / span >
< / code > < / pre >
< p > Helper to get only the route names for matches:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > defn< / span > < / span > name-path [router path]
(< span class = "hljs-name" > some-> > < / span > (< span class = "hljs-name" > recursive-match-by-path< / span > router path)
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > mapv< / span > < / span > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > comp< / span > < / span > < span class = "hljs-symbol" > :name< / span > < span class = "hljs-symbol" > :data< / span > ))))
(< span class = "hljs-name" > name-path< / span > router < span class = "hljs-string" > " /olipa/kerran/avaruus" < / span > )
< span class = "hljs-comment" > ; [:olipa :kerran :avaruus]< / span >
2018-07-18 10:13:28 +00:00
< / code > < / pre >
2018-07-25 07:56:39 +00:00
< h2 id = "dynamic-routing" > Dynamic routing< / h2 >
< p > In all the examples above, the routers were created ahead of time, making the whole route tree effective static. To do dynamic routing, we should use router references so that we can update the routes either on background or per request basis. Let' s walk through both cases.< / p >
< p > First, we need to modify our matching function to support router references:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > defn-< / span > < / span > < < [x]
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > if< / span > < / span > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > instance?< / span > < / span > clojure.lang.IDeref x) (< span class = "hljs-name" > < span class = "hljs-builtin-name" > deref< / span > < / span > x) x))
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > defn< / span > < / span > recursive-match-by-path [router path]
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > if-let< / span > < / span > [match (< span class = "hljs-name" > r/match-by-path< / span > (< span class = "hljs-name" > < < < / span > router) path)]
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > if-let< / span > < / span > [subrouter (< span class = "hljs-name" > < span class = "hljs-builtin-name" > -> < / span > < / span > match < span class = "hljs-symbol" > :data< / span > < span class = "hljs-symbol" > :router< / span > < < )]
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > if-let< / span > < / span > [submatch (< span class = "hljs-name" > recursive-match-by-path< / span > subrouter (< span class = "hljs-name" > subs< / span > path (< span class = "hljs-name" > str/last-index-of< / span > (< span class = "hljs-symbol" > :template< / span > match) < span class = "hljs-string" > " /" < / span > )))]
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > into< / span > < / span > [match] submatch))
[match])))
< / code > < / pre >
< p > A router that can be updated on demand, for example based on a domain event when a new entry in inserted into a database. We' ll wrap the router into a < code > atom< / code > to achieve this.< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > def< / span > < / span > beer-router
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > atom< / span > < / span >
(< span class = "hljs-name" > r/router< / span >
[[< span class = "hljs-string" > " /lager" < / span > < span class = "hljs-symbol" > :lager< / span > ]])))
< / code > < / pre >
< p > Another router, which is re-created on each routing request.< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > def< / span > < / span > dynamic-router
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > reify< / span > < / span > clojure.lang.IDeref
(< span class = "hljs-name" > < span class = "hljs-builtin-name" > deref< / span > < / span > [_]
(< span class = "hljs-name" > r/router< / span >
[< span class = "hljs-string" > " /duo" < / span > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > keyword< / span > < / span > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > gensym< / span > < / span > < span class = "hljs-string" > " " < / span > ))]))))
< / code > < / pre >
< p > Now we can compose the routers into a system-level static root router.< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > def< / span > < / span > router
(< span class = "hljs-name" > r/router< / span >
[[< span class = "hljs-string" > " /gin/napue" < / span > < span class = "hljs-symbol" > :napue< / span > ]
[< span class = "hljs-string" > " /ciders/*" < / span > < span class = "hljs-symbol" > :ciders< / span > ]
[< span class = "hljs-string" > " /beers/*" < / span > {< span class = "hljs-symbol" > :name< / span > < span class = "hljs-symbol" > :beers< / span >
< span class = "hljs-symbol" > :router< / span > beer-router}]
[< span class = "hljs-string" > " /dynamic/*" < / span > {< span class = "hljs-symbol" > :name< / span > < span class = "hljs-symbol" > :other< / span >
< span class = "hljs-symbol" > :router< / span > dynamic-router}]]))
< / code > < / pre >
< p > Matching root routes:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > name-path< / span > < span class = "hljs-string" > " /vodka/russian" < / span > )
< span class = "hljs-comment" > ; nil< / span >
(< span class = "hljs-name" > name-path< / span > < span class = "hljs-string" > " /gin/napue" < / span > )
< span class = "hljs-comment" > ; [:napue]< / span >
< / code > < / pre >
< p > Matching (nested) beer routes:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > name-path< / span > < span class = "hljs-string" > " /beers/lager" < / span > )
< span class = "hljs-comment" > ; [:beers :lager]< / span >
(< span class = "hljs-name" > name-path< / span > < span class = "hljs-string" > " /beers/saison" < / span > )
< span class = "hljs-comment" > ; nil< / span >
< / code > < / pre >
< p > No saison!? Let' s add the route:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > swap!< / span > < / span > beer-router add-routes [[< span class = "hljs-string" > " /saison" < / span > < span class = "hljs-symbol" > :saison< / span > ]])
< / code > < / pre >
< p > There we have it:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > name-path< / span > < span class = "hljs-string" > " /beers/saison" < / span > )
< span class = "hljs-comment" > ; [:beers :saison]< / span >
< / code > < / pre >
< p > We can' t add a conflicting routes:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > < span class = "hljs-builtin-name" > swap!< / span > < / span > beer-router add-routes [[< span class = "hljs-string" > " /saison" < / span > < span class = "hljs-symbol" > :saison< / span > ]])
< span class = "hljs-comment" > ;CompilerException clojure.lang.ExceptionInfo: Router contains conflicting route paths:< / span >
< span class = "hljs-comment" > ;< / span >
< span class = "hljs-comment" > ; /saison< / span >
< span class = "hljs-comment" > ;-> /saison< / span >
< / code > < / pre >
< p > The dynamic routes are re-created on every request:< / p >
< pre > < code class = "lang-clj" > (< span class = "hljs-name" > name-path< / span > < span class = "hljs-string" > " /dynamic/duo" < / span > )
< span class = "hljs-comment" > ; [:other :2390883]< / span >
(< span class = "hljs-name" > name-path< / span > < span class = "hljs-string" > " /dynamic/duo" < / span > )
< span class = "hljs-comment" > ; [:other :2390893]< / span >
2018-07-18 10:13:28 +00:00
< / code > < / pre >
2018-07-25 07:56:39 +00:00
< h3 id = "performance" > Performance< / h3 >
< p > With nested routers, instead of having to do just one route match, matching is recursive, which adds a small cost. All nested routers need to be of type catch-all at top-level, which is order of magnitude slower than fully static routes. Dynamic routes are the slowest ones, at least an order of magnitude slower, as the router needs to be recreated for each request.< / p >
< p > Here' s a quick benchmark on the recursive matches.< / p >
< table >
< thead >
< tr >
< th > path< / th >
< th > time< / th >
< th > type< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > < code > /gin/napue< / code > < / td >
< td > 40ns< / td >
< td > static< / td >
< / tr >
< tr >
< td > < code > /ciders/weston< / code > < / td >
< td > 440ns< / td >
< td > catch-all< / td >
< / tr >
< tr >
< td > < code > /beers/saison< / code > < / td >
< td > 600ns< / td >
< td > catch-all + static< / td >
< / tr >
< tr >
< td > < code > /other/drink< / code > < / td >
< td > 12000ns< / td >
< td > catch-all + dynamic< / td >
< / tr >
< / tbody >
< / table >
< p > In this example, we could have wrapped the top-level router in an < code > atom< / code > and add the beer-routes directly to it, making them order of magnitude faster.< / p >
2018-07-18 10:13:28 +00:00
< h2 id = "todo" > TODO< / h2 >
< ul >
2018-07-25 07:56:39 +00:00
< li > example how to do dynamic routing with < code > reitit-ring< / code > < / li >
< li > create a < code > recursive-router< / code > into a separate ns with all < code > r/routes< / code > implemented correctly?< / li >
2018-07-18 10:13:28 +00:00
< li > < code > reitit.core/merge-routes< / code > to effectively merge routes with route data< / li >
< / ul >
< / section >
< / div >
< div class = "search-results" >
< div class = "has-results" >
< h1 class = "search-results-title" > < span class = 'search-results-count' > < / span > results matching "< span class = 'search-query' > < / span > "< / h1 >
< ul class = "search-results-list" > < / ul >
< / div >
< div class = "no-results" >
< h1 class = "search-results-title" > No results matching "< span class = 'search-query' > < / span > "< / h1 >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< a href = "configuring_routers.html" class = "navigation navigation-prev " aria-label = "Previous page: Configuring Routers" >
< i class = "fa fa-angle-left" > < / i >
< / a >
< a href = "different_routers.html" class = "navigation navigation-next " aria-label = "Next page: Different Routers" >
< i class = "fa fa-angle-right" > < / i >
< / a >
< / div >
< script >
var gitbook = gitbook || [];
gitbook.push(function() {
2018-07-25 07:56:39 +00:00
gitbook.page.hasChanged({"page":{"title":"Composing Routers","level":"1.4.2","depth":2,"next":{"title":"Different Routers","level":"1.4.3","depth":2,"path":"advanced/different_routers.md","ref":"advanced/different_routers.md","articles":[]},"previous":{"title":"Configuring Routers","level":"1.4.1","depth":2,"path":"advanced/configuring_routers.md","ref":"advanced/configuring_routers.md","articles":[]},"dir":"ltr"},"config":{"plugins":["editlink","github","highlight"],"root":"doc","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"editlink":{"label":"Edit This Page","multilingual":false,"base":"https://github.com/metosin/reitit/tree/master/doc"},"github":{"url":"https://github.com/metosin/reitit"},"highlight":{},"search":{},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"gitbook":"*"},"file":{"path":"advanced/composing_routers.md","mtime":"2018-07-25T07:55:47.085Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2018-07-25T07:56:33.253Z"},"basePath":"..","book":{"language":""}});
2018-07-18 10:13:28 +00:00
});
< / script >
< / div >
< script src = "../gitbook/gitbook.js" > < / script >
< script src = "../gitbook/theme.js" > < / script >
< script src = "../gitbook/gitbook-plugin-editlink/plugin.js" > < / script >
< script src = "../gitbook/gitbook-plugin-github/plugin.js" > < / script >
< script src = "../gitbook/gitbook-plugin-search/search-engine.js" > < / script >
< script src = "../gitbook/gitbook-plugin-search/search.js" > < / script >
< script src = "../gitbook/gitbook-plugin-lunr/lunr.min.js" > < / script >
< script src = "../gitbook/gitbook-plugin-lunr/search-lunr.js" > < / script >
< script src = "../gitbook/gitbook-plugin-sharing/buttons.js" > < / script >
< script src = "../gitbook/gitbook-plugin-fontsettings/fontsettings.js" > < / script >
< / body >
< / html >