+
+ Coercion Explained
+Coercion is a process of transforming parameters (and responses) from one format into another. Reitit separates routing and coercion into separate steps.
+By default, all wildcard and catch-all parameters are parsed as Strings:
+(require '[reitit.core :as r])
+
+(def router
+ (r/router
+ ["/:company/users/:user-id" ::user-view]))
+
+Here's a match with the String :params:
+(r/match-by-path r "/metosin/users/123")
+
+
+
+
+
+
+To enable parameter coercion, we need to do few things:
+
+- Define a
Coercion for the routes
+- Define types for the parameters
+- Compile coercers for the types
+- Apply the coercion
+
+Define Coercion
+reitit.coercion/Coercion is a protocol defining how types are defined, coerced and inventoried.
+Reitit has the following coercion modules:
+
+Coercion can be attached to route data under :coercion key. There can be multiple Coercion implementation into a single router, normal scoping rules apply.
+Defining parameters
+Route parameters can be defined via route data :parameters. It has keys for different type of parameters: :query, :body, :form, :header and :path. Syntax for the actual parameters is defined by the Coercion.
+Here's the example with Schema path-parameters:
+(require '[reitit.coercion.schema])
+(require '[schema.core :as s])
+
+(def router
+ (r/router
+ ["/:company/users/:user-id" {:name ::user-view
+ :coercion reitit.coercion.schema/coercion
+ :parameters {:path {:company s/Str
+ :user-id s/Int}}}]))
+
+Routing again:
+(r/match-by-path r "/metosin/users/123")
+
+
+
+
+
+
+
+
+
+Coercion was not applied. Why? All we did was just added more data to the route and the routing functions are just responsible for routing, not coercion.
+But now we should have enough data on the match to apply the coercion.
+Compiling coercers
+Before the actual coercion, we need to compile the coercers against the route data. This is because compiled coercers yield much better performance. A separate step makes thing explicit and non-magical. Compiling could be done via a Middleware, Interceptor but we can also do it at Router-level, effecting all routes.
+There is a helper function for the coercer compiling in reitit.coercion:
+(require '[reitit.coercion :as coercion])
+(require '[reitit.coercion.schema])
+(require '[schema.core :as s])
+
+(def router
+ (r/router
+ ["/:company/users/:user-id" {:name ::user-view
+ :coercion reitit.coercion.schema/coercion
+ :parameters {:path {:company s/Str
+ :user-id s/Int}}}]
+ {:compile coercion/compile-request-coercers}))
+
+Routing again:
+(r/match-by-path r "/metosin/users/123")
+
+
+
+
+
+
+
+
+
+The compiler added a :result key into the match (done just once, at router creation time), which holds the compiled coercers. We are almost done.
+Applying coercion
+We can use a helper function to do the actual coercion, based on a Match:
+(coercion/coerce!
+ (r/match-by-path router "/metosin/users/123"))
+
+
+If a coercion fails, a typed (:reitit.coercion/request-coercion) ExceptionInfo is thrown, with descriptive data about the actual error:
+(coercion/coerce!
+ (r/match-by-path router "/metosin/users/ikitommi"))
+
+
+
+
+
+Full example
+Here's an full example of routing + coercion.
+(require '[reitit.coercion.schema])
+(require '[reitit.coercion :as coercion])
+(require '[schema.core :as s])
+
+(def router
+ (r/router
+ ["/:company/users/:user-id" {:name ::user-view
+ :coercion reitit.coercion.schema/coercion
+ :parameters {:path {:company s/Str
+ :user-id s/Int}}}]
+ {:compile coercion/compile-request-coercers}))
+
+(defn route-and-coerce! [path]
+ (if-let [match (r/match-by-path router path)]
+ (assoc match :parameters (coercion/coerce! match))))
+
+(route-and-coerce! "/metosin/users/123")
+
+
+
+
+
+
+
+
+
+
+(route-and-coerce! "/metosin/users/ikitommi")
+
+
+Ring Coercion
+For a full-blown http-coercion, see the ring coercion.
+Thanks to
+Most of the thing are just polished version of the original implementations. Big thanks to:
+
+
+
+