-
Notifications
You must be signed in to change notification settings - Fork 150
Routing
Compojure-api uses Compojure for routing.
The big difference is, that all compojure-api route functions & macros return compojure.api.routes/Route
-records which can both act as normal ring handlers (e.g. can be called with request to produce an response) and they satisfy the compojure.api.routes/Routing
protocol for collecting the route information.
(def inc-route
(GET "/inc" []
:query-params [x :- s/Int]
:return {:result s/Int}
(ok {:result (inc x)})))
inc-route
; #Route{:path "/inc",
; :method :get,
; :info {:parameters {:query {Keyword Any, :x Int}},
; :responses {200 {:schema {:result Int}, :description ""}}},
; :childs nil,
; :handler #function[compojure.core/wrap-routes/fn--19752]}
(inc-route {:request-method :get, :uri "/inc" :query-params {}})
; CompilerException clojure.lang.ExceptionInfo: Request validation failed: {:x missing-required-key}
(inc-route {:request-method :get, :uri "/inc" :query-params {:x 1}})
; {:status 200, :headers {}, :body {:result 2}, :compojure.api.meta/serializable? true}
At api creation time, the route-tree is walked and the reverse-route tree is generated - to be used both for swagger-docs & for bi-directional routing.
(compojure.api.routes/get-routes
(context "/api" []
inc-route
(POST "/mortem" []
:summary "oh, noes"
(ok {:rest "in piece"}))))
; [["/api/inc" :get {:parameters {:query {Keyword Any, :x Int}}, :responses {200 {:schema {:result Int}, :description ""}}}]
; ["/api/mortem" :post {:summary "oh, well"}]]
(Reverse-)routing information is injected into the request by the api
and thus available for all api-routes at runtime. Handlers can use this for the reverse routing using the path-for
macro (or the underlaying path-for*
function). Both methods take an extra parameters map, which will be used to populate the possible path-parameters.
(def app
(api
(GET "/pong" []
:name ::pong
(ok))
(GET "/ping" []
(temporary-redirect (path-for ::pong)))))
(app {:request-method :get, :uri "/ping"})
; {:status 307, :headers {"Location" "/pong"}, :body ""}
If the api routes contain routes, which do not satisfy the Routing
protocol (e.g. normal ring/compojure functions), an callback-function [:api :invalid-routes-fn]
is called. By default, an warning is logged. At runtime, everything works as expected.
By default, a WARN is logged.
(api
(GET "/ping" []
(ok {:message "I satisfy the Routing protocol!"}))
(compojure.core/GET "/pong" []
(ok {:message "I dont."})))
; WARN Not all child routes satisfy compojure.api.routing/Routing. {:path nil, :method nil}, invalid child routes: [#function[compojure.core/if-method/fn--19598]]
Marking routes undocumented
will stop the route collector to entering those routes (still works at runtime thou).
(api
(GET "/ping" []
(ok {:message "I satisfy the Routing protocol!"}))
(undocumented
(compojure.core/GET "/pong" []
(ok {:message "I dont."}))))
You can also mark compojure-api routes as undocumented
- here, the whole api is undocumented
(api
(undocumented
(GET "/ping" []
(ok {:message "I satisfy the Routing protocol!"}))
(compojure.core/GET "/pong" []
(ok {:message "I dont."}))))
One can also change how the api handles non-compojure-api routes. Here, we break at compile-time:
(api
{:api {:invalid-routes-fn compojure.api.routes/fail-on-invalid-child-routes}}
(GET "/ping" []
(ok {:message "I satisfy the Routing protocol!"}))
(compojure.core/GET "/pong" []
(ok {:message "I dont."})))
; CompilerException clojure.lang.ExceptionInfo: Not all child routes satisfy compojure.api.routing/Routing. {:path nil, :method nil, :invalid [#function[compojure.core/if-method/fn--19598]]}
... or just ignore the bad routes
(api
{:api {:invalid-routes-fn nil}}
(GET "/ping" []
(ok {:message "I satisfy the Routing protocol!"}))
(compojure.core/GET "/pong" []
(ok {:message "I dont."})))
To set up a "didn't match anything" handler within an api
, just do like you would do with ring/compojure.
(api
inc-route
(undocumented
(compojure.route/not-found (ok {:not "found"}))))