0.5.0 docs





    Nov 14, 2017

    Christopher Brown
    University of Texas at Austin
    Austin, TX


    Index of all namespaces

    « Project + dependencies

    URL-driven routes handling

    Core data-structure-driven routing.
    The main functions to call are:
        (resolve-endpoint routes {:path string})
        (generate-path routes {:endpoint form, ...params})
    The main protocols to extend are Routes and Pattern.
    Extra wrappers and customizers for Routes and Pattern structures.
    This module should be considered private to routes
    Tools for listing endpoints & route descriptions.
    The README below is fetched from the published project artifact. Some relative links may be broken.


    Clojars Project Travis CI Build Status Coverage Status

    routes for Clojure is inspired and heavily influenced in implementation by bidi, but works more like a language than a regular expression.

    bidi is opinionated about your routes’ endpoints.

    With bidi, suppose you have this route structure:

    ["/" {"users" {""        :users
                   ["/" :id] :users}}]

    You’d want the path for :users to be /users and the path for :users with :params {:id 7} to be /users/7.

    Because bidi uses greedy search, this doesn’t work. In the latter case, it finds the first endpoint that matches the given target, and serializes the components that led to that endpoint (discarding the excess route parameter), resulting in /users.

    Suppose instead you had this ordering:

    ["/" {"users" {["/" :id] :users
                   ""        :users}}]

    This would produce the correct path for :users with :params {:id 7}, /users/7, but it would throw an exception when serializing the path for just :users, since it would run with the first match, which requires an :id parameter, and there is none such for the index route.

    Another use-case is to contextualize a whole group of routes. For example, suppose you have some routes:

    (def store-resource-routes
      {"/customers" {["/" :id] :customers
                     ""        :customers}
       "/products"  {["/" :id] :products
                     ""        :products}})

    And you wanted to merge and group these under two paths, /api/v1 and /api/v2, but not write the whole thing out twice. Due to the greedy nature of bidi’s endpoint matching, this would be impossible; you could never generate paths for any of the api versions besides the first, and you could not tell which api version a path used from the matched-route’s :route-params.

    In this library’s routes.extra module, there is a (parameterize pattern key value) function that wraps a pattern structure in an extension of the Pattern protocol, and adds a (required) parameter to all the endpoints under that pattern. This function creates a new instance of the ParameterizedPattern record, and, when generating a path, requires the route context to match the specified {key value} pair, and adds that same pair to the route context when resolving the endpoint from a path. Your routes would look like this:

    (def routes
      ["/api/" {(parameterize "v1" :api-version 1) store-resource-routes
                (parameterize "v2" :api-version 2) store-resource-routes}])

    With these routes, we can generate the path for :customers with parameters :api-version 1:

    (generate-path routes {:endpoint :customers :api-version 1})
    ;=> "/api/v1/customers"

    As mentioned, the parameters must be provided, or else there’s no match.

    (generate-path routes {:endpoint :customers})
    ;=> nil

    And we can resolve endpoints that specify the parameters used to match the given path:

    (resolve-endpoint {:path "/api/v2/customers/chb"})
    ;=> {:endpoint :customers :api-version 2 :id "chb"}

    Differences from bidi

    Failed matches. bidi throws on failed matches, routes returns nil.

    Naming * Protocols - PatternPattern (no change) - MatchedRoutes * functions - match-route / match-route* / match-pair / resolve-handlerresolve-endpoint (yep, only one function to handle them all) - match-patternmatch-pattern (no change) - path-for / unmatch-pair / unresolve-handlergenerate-path (simple as that!) - unmatch-patterngenerate-pattern-path (though this is for patterns and you probably won’t call it directly)

    Quick-start example

    (ns user
      (:require [routes.core :refer [resolve-endpoint generate-path]]))
    (def routes
      {"/login" :login
       ["/wiki/" :page] :wiki})
    (resolve-endpoint routes {:path "/wiki/Welcome"})
    ;=> {:endpoint :wiki :page "Welcome"}
    (generate-path routes {:endpoint :login})
    ;=> "/login"
    (generate-path routes {:endpoint :wiki :page "Contact"})
    ;=> "/wiki/Contact"


    There are a lot of data-driven (i.e., routes-as-data, as opposed to imperative Compojure-style) cross-platform routing libraries out there.

    • bidi (**bi**-**di**rectional “URI dispatch”)
    • bidi was not the first data-driven router library (first commit was 2013-12-20), but it’s been the most successful.
    • It’s one of the simplest, perhaps due to being one of the most opinionated / restrictive.
    • This library is most directly derived from bidi; see above for the important differences.
    • reitit (“reitit” is Finnish for “routes”)
    • Very much like bidi, but with more introspection about the path.
    • Perhaps the most similar of these libraries, in spirit, to routes, though I started from bidi (reitit’s first commit was 2017-08-07, routes’s was 2017-08-24)
    • Great documentation!
    • ataraxy, from Mr. Compojure himself.
    • Very much like bidi, but focused on Ring integration
    • More support for dispatching on properties of the full HTTP request, as opposed to just the URI/path
    • First commit: 2015-12-12
    • guduGenerate URL, Degenerate URL”
    • Very much like bidi, but more of a DSL, oriented around URL conventions (e.g., slashes separate path components)
    • First commit: 2013-03-10
    • bide
    • Very much like bidi, but patterns are described as strings rather than data structures (e.g., bidi’s ["/account/" [[[:account-uuid "/" :page-uuid] :account/page]]] becomes bide’s ["/account/:account-uuid/:page-uuid" :account/page]).
    • Tighter integration with the browser: “It [bide] uses goog.History API under the hood”
    • First commit: 2016-08-27
    • silk “Isomorphic Clojure[Script] Routing”
    • Very much like bidi, but with more helper functions for preparing path components, e.g. (silk/int :id)
    • First commit: 2014-06-28


    Compile production JavaScript output:

    lein cljsbuild once production


    Run Clojure tests:

    lein with-profile test test

    Compute Clojure test coverage:

    lein with-profile test cloverage

    Run the ClojureScript tests:

    lein with-profile test doo rhino test once

    Run the ClojureScript tests on Chrome:

    npm install -g karma-cli
    npm install karma karma-cljs-test karma-chrome-launcher
    lein with-profile test doo chrome test once


    Copyright © 2017 Christopher Brown. Eclipse Public License - v 1.0.