reitit

https://cljdoc.org/d/metosin/reitit/ https://github.com/metosin/reitit/
2020-10-30T12:21:03.217300Z

i'm mirroring https://github.com/metosin/reitit/blob/master/examples/ring-malli-swagger/src/example/server.clj as much as I can but I'm not getting coercion. I'm getting validation , but no coercion of request body.

Eric Ihli 2020-10-30T14:03:14.218Z

I don't know if this is related, but for that second argument to `ring/router`, if I pass `{:compile coercion/compile-request-coercers ,,,`, then I get coercion. If I pass it as `{:data {:compile coercion/compile-request-coercers ,,,` then I get an error that `reitit.ring.Endpoint cannot be cast to clojure.lang.IFn`. Every example I see has :compile nested under :data. So I'm trying to figure out why mine works when not nested and breaks when nested. Are both supposed to be supported? Was there an update and one is legacy?

2020-10-30T14:09:10.218200Z

I've tried that (compile on :data) but I've got 405 method not allowed on my requests

Eric Ihli 2020-10-30T14:10:19.218400Z

Yeah I've run into that too. But only on requests that don't use coercion. Still trying to figure it out.

(defn router [db]
  (ring/router
   [["/" {:handler user-ctl/default}]
    ["/reset" {:handler user-ctl/reset-changes}]
    ["/user"
     ["/list" {:handler user-ctl/get-users}]
     ["/form" {:handler user-ctl/edit}]
     ["/form/:id" {:parameters {:path {:id int?}}
                   :coercion reitit.coercion.spec/coercion
                   :get {:handler user-ctl/edit}}]
     ["/save" {:post {:handler user-ctl/save}}]
     ["/delete/:id" {:get {:handler user-ctl/delete-by-id}
                     :coercion reitit.coercion.spec/coercion
                     :parameters {:path {:id int?}}}]]]
   {:compile coercion/compile-request-coercers
    :data {:db db
           :middleware [my-middleware
                        coerce-request-middleware
                        parameters/parameters-middleware
                        wrap-keyword-params
                        middleware-db]}}))

Eric Ihli 2020-10-30T14:10:54.218700Z

I can do match and coerce /user/form/3 for example. But /reset and / result in a 405.

Eric Ihli 2020-10-30T14:11:39.218900Z

It's like that compile key is causing routes with a :coercion key to work but every other route to break.

Eric Ihli 2020-10-30T14:15:20.219300Z

Ugh... Ok.

["/list" {:get {:handler user-ctl/get-users}}]

Eric Ihli 2020-10-30T14:16:01.219600Z

Adding that :get key to to the route got rid of the 405. So... I guess when your using coercion, you must manually specify methods for routes that don't have a :parameters and :coercion keys? Nope... dirty repl.

ikitommi 2020-10-30T16:05:56.220500Z

@eyaldocs no default way to do that, as response coercion happens in mw chain after handler has returned

ikitommi 2020-10-30T16:06:34.220700Z

you could add a custom mw into the chain before the response-coercion to do that

ikitommi 2020-10-30T16:14:57.221200Z

["/api" {:get {:handler ..., :post-handler (fn [res] ....log the data... res)}]

ikitommi 2020-10-30T16:15:50.221400Z

you could also make the :post-handler accept a vector of fns and compose those using comp

ikitommi 2020-10-30T16:18:50.221600Z

I think there should be in-built support for this. About to need it myself. Also, the frontend schemas should be precompiled in router creation for perf. Can't recall if that is the case today. PRs welcome!

ikitommi 2020-10-30T16:21:07.221800Z

reitit does not touch the original parameters. Muktipart-params are under :mulipart-params I recall. Reitit writes the coerced parameters under :parameters so one can access both raw and coerced

ikitommi 2020-10-30T16:21:45.222Z

coercion should happen to multipart paramus too, PR welcome if not so in examples

ikitommi 2020-10-30T16:23:41.222200Z

sounds resolved? great!

2020-10-30T17:14:14.222500Z

@ikitommi I've tried the example posting x: "1" y: "1" and it does to coerce body parameters to 1. What can I do to coerce body parameters ?

ikitommi 2020-10-30T17:42:27.222700Z

JSON coercion doen't coerce string->number, as JSON supports numbers. If you need that, you have to swap the json- body coercion to use String Coercion. Can be done by creating a coercion impl with options. There should be example out somewhere. Sorry, the docs could be better

1👍
Eric Ihli 2020-10-30T17:50:04.222900Z

No. Thought it was resolved, but my REPL was in a state where the issue wouldn't present itself.

ingesol 2020-10-30T18:17:47.223100Z

That is the case currently, for coercion in

reitit-frontend/match-by-path
But not in
reitit/match-by-name
Maybe I’m misunderstanding something, but coercion in reitit seems to mean string->something. Maybe that’s correct, converting values to strings isn’t coercion, but rather encoding?

ingesol 2020-10-30T18:21:02.223300Z

I do have my schemas precompiled, I just don’t see the reitit api that will use them. And I suspect it doesn’t exist. I will try to see if I can parse the match-by-path code to see how it accesses compiled schemas in the router

2020-10-30T19:14:44.224100Z

(def custom-coercion (reitit.coercion.malli/create { :transformers {:body {:default reitit.coercion.malli/string-transformer-provider :formats {"application/json" reitit.coercion.malli/string-transformer-provider}} :string {:default reitit.coercion.malli/string-transformer-provider} :response {:default reitit.coercion.malli/default-transformer-provider}} ;; set of keys to include in error messages :error-keys #{#_:type :coercion :in :schema :value :errors :humanized #_:transformed} ;; schema identity function (default: close all map schemas) :compile malli.util/closed-schema ;; strip-extra-keys (effects only predefined transformers) :strip-extra-keys true ;; add/set default values :default-values true ;; malli options :options nil}) ) @ikitommi with the information you provided I've found https://github.com/metosin/reitit/issues/288 and with this custom-coercion it worked.

2020-10-30T20:01:20.225700Z

hey everyone i am new to clj + cljs. i am attempting to use reitit in a cljs project. i have my routes defined in a separate cljs file but i get this error when trying to refer to a route in another file

2020-10-30T20:02:16.226Z

in my view file i am using this

[:a.button.is-danger {:href (rfe/href ::create-job)} "Post a Job"]
it says that it cannot find ::create-job but in my routes file i have this
(def routes
  [["/"
    {:name ::frontpage
     :view home-view/home-page}]

   ["/create"
    {:name ::create-job
     :view create-job-view/root}]])

Eric Ihli 2020-10-30T20:20:58.226200Z

I don't have high confidence that what I'm about to say is related to your issue, but I see you say "new to clj" and I see a namespaced keyword ::create-job and I remember the times I ran into problems due to namespace issues. Are those routes defined in client.home.view? I see the error message say that it can't find client.home.view/create-job. The double colon before a keyword namespaces it. so ::create-job becomes :http://whatever.namespace.its.in/create-job`.

Eric Ihli 2020-10-30T20:21:29.226400Z

So if you use ::create-job in two different files (i.e. namespaces (in general)) then it will be two different values.

2020-10-30T20:22:23.226700Z

ohhhhhhhhhhhh

2020-10-30T20:23:21.226900Z

the routes are defined in client.api

2020-10-30T20:23:25.227100Z

(ns client.api
  (:require [client.home.view :as home-view]
            [client.job.create.view :as create-job-view]))

(def routes
  [["/"
    {:name :frontpage
     :view #'home-view/home-page}]

   ["/create"
    {:name :create-job
     :view #'create-job-view/root}]])

2020-10-30T20:23:42.227300Z

(just changing things around rn per you comment and what im seeing on the web)

Eric Ihli 2020-10-30T20:24:24.227500Z

Oh. Well I see in that code you just pasted :create-job is not namespaced. Try namespacing it, ::create-job in client.api and then try accessing it from your view as :client.api/create-job.

Eric Ihli 2020-10-30T20:24:37.227700Z

Or maybe de-namespace it everywhere.

2020-10-30T20:26:49.227900Z

I have http://app.ui.contratualizacao.menu on which I define : ::contrato which expands to http://app.ui.contratualizacao.menu/contrato. On other file i need to refer to that name. I require [http://app.ui.contratualizacao.menu :as menu-contr] and refer as ::menu-contr/contrato.

2020-10-30T20:27:29.228100Z

Or you can be explicit like @ericihli is suggesting

2020-10-30T20:27:54.228400Z

ah i got it!

2020-10-30T20:28:03.228600Z

i router is

(ns client.api
  (:require [client.home.view :as home-view]
            [client.job.create.view :as create-job-view]))

(def routes
  [["/"
    {:name ::frontpage
     :view home-view/home-page}]

   ["/create"
    {:name ::create-job
     :view create-job-view/root}]])

2020-10-30T20:28:15.228800Z

my call for the route is

2020-10-30T20:28:16.229Z

[:a.button.is-danger {:href (rfe/href :create-job)} "Post a Job"]

2020-10-30T20:28:28.229200Z

prior i had

[:a.button.is-danger {:href (rfe/href ::create-job)} "Post a Job"]

2020-10-30T20:28:42.229400Z

::create-job vs. :create-job in the href

2020-10-30T20:28:52.229600Z

thanks everyone for the help and rubber ducking!

Eric Ihli 2020-10-30T23:51:07.230100Z

Ok. I haven't pinpointed exactly what part of the code causes this to be the case, but I have found a set of options that works.

Eric Ihli 2020-10-30T23:52:24.230300Z

My understanding is that when a routers options includes a compile coercion/compile-request-coercers key/value, then any route that doesn't include a {:coercion reitit.coercion.spec/coercion :parameters {,,,}} will get excluded from the router.

Eric Ihli 2020-10-30T23:53:07.230500Z

It's like a router that compiles coercion routes must ONLY include coerced routes. Other routes don't get matched against.