reitit

valerauko 2025-06-23T14:45:04.200929Z

Is there some trick to getting malli coercion working in reitit frontend?

valerauko 2025-06-23T14:49:06.773289Z

I have a route like this

["/profile/{user-id}"
 {:name ::profile
  :coercion rcm/coercion
  :parameters {:path {:user-id pos-int?}}
  :view #'profile/view}]
I start my router with reitit.frontend.easy and when I inspect the new match in the navigation callback, the :parameters :path :user-id is a string, not decoded as an integer

valerauko 2025-06-23T14:49:07.772269Z

Is there some puzzle piece i'm missing here?

Roma 2025-06-23T15:01:25.566089Z

We use something like this:

(rf/router
    ["/"
     ...]
    {:data    {:coercion rcs/coercion}
     :compile coercion/compile-request-coercers})

2025-06-23T15:11:38.180409Z

does it coerce if you do something like:

:parameters {:path [:map [:user-id :int]]}

2025-06-23T15:12:19.609649Z

oh and I noticed your path should probably be like "/profile/:user-id" instead

Roma 2025-06-23T15:25:34.998749Z

Yes, coercion works, we use specs though, not malli:

["companies/:company-id"
      {:name ::campaign-creation
       :view :campaign-creation
       :parameters {:path {:company-id int?}}}]

2025-06-23T15:28:55.282739Z

I meant for @vale I use malli with coercion, but with the format I showed above, and it works

Roma 2025-06-23T15:32:22.341809Z

ah, sorry 🙂 yeah, rcm/coercion looks like Malli coercion, but I'm not sure that {:user-id pos-int?} is a Malli syntax (although there is a map syntax as I recall). Regarding :user-id vs {user-id}, both are valid.

👍 1
valerauko 2025-06-23T16:51:53.764319Z

the problem was that my router in the frontend was built with reitit.ring/router (for extracting api methods and automating request generation) instead of reitit.frontend/router when i swap it for the reitit.frontend router coercion worked as expected... except i lose all the api method information instead. i'm gonna try figure out what's the "right" way to do this... (shared routes between api and frontend)

2025-06-23T15:20:02.745289Z

Is it me or is the Reitit exception middleware checking hierarchies in the wrong direction? I was assuming that I can throw (ex-info "" {:type :derived}) and handle all derived types with a common parent after (derive :derived :parent):

(derive ::derived ::super)
(let [handlers (merge exception/default-handlers {::super (constantly {:status 401})})
      middleware (exception/create-exception-middleware handlers)
      handler (fn [_] (throw (ex-info "test" {:type ::derived})))
      h (reitit.middleware/chain [middleware] handler)]
  (h {})) ;; {:status 500}
As you can see, my custom handler is not called. Switching the parent-child relationship works:
(underive ::derived ::super) 

(derive ::super ::derived)
(let [handlers (merge exception/default-handlers {::super (constantly {:status 401})})
      middleware (exception/create-exception-middleware handlers)
      handler (fn [_] (throw (ex-info "test" {:type ::derived})))
      h (reitit.middleware/chain [middleware] handler)]
  (h {})) ;; {:status 401}

DrLjótsson 2025-06-23T19:54:45.508699Z

I think it would be nice if route syntax would allow colons in brackets, like "/users/{:user/id}" So one can specify namespaced keywords (which requires brackets) but still have the visual recognition of keywords. https://github.com/metosin/reitit/issues/748

mkvlr 2025-06-23T20:11:40.081799Z

agree, I’ve always been confused by the lack of the colon for the namespaced keyword syntax

👍 4