Fork me on GitHub
#reitit
<
2024-02-15
>
Ernesto Garcia11:02:34

It is tricky when cljs clients to send numbers over transit to a server, as the number maybe decoded on the server side as a Long or as a Double, which will make server validation fail occasionally. (Using malli):

["/settings" {:put {:handler #'put-settings
                    :parameters {:body [:settings/threshold :double]}}}]
What would you suggest for setting up a default transformation for coercing any number type to a double if :double is specified as the type?

Ernesto Garcia11:02:37

I have tried with what seems a too-difficult setup:

wevrem15:02:06

Does the above work? I couldn’t get malli to work with :body-params, which was where muutaja puts decoded content. But based on what you did above, I guess I just wasn’t sophisticated enough. I have two solutions I currently employ: 1. I have a coerce-double function that I deploy, either as middleware on my routes, or as a transformation step on my data before transacting it into datomic. 2. On my front-end, within a custom element, I store and transmit a com.cognitect.transit.types/taggedValue in order to simulate a big decimal. I could probably do something like № 2 with a javascript number. As of yet I haven’t tried it because I have № 1 for dealing with doubles on the backend. But I guess I could. Instead of handing a javascript number over to transit and having no control over whether it encodes as a long or a double, I could wrap it in a taggedValue and force it to be decoded as a double.

Ernesto Garcia09:02:16

I guess I just wasn’t sophisticated enoughThe code above is not sophisticated. It just takes the default transformer built by Reitit and adds one. It's just a lot of plumbing 😊. I should have written it with some requires. > I have a coerce-double function that I deploy, either as middleware on my routes That function takes the body schema into account? It could be a nice alternative, instead of messing with Reitit's use of Malli, which doesn't seem to be made for extending/customizing. > On my front-end, within a custom element, I store and transmit a com.cognitect.transit.types/taggedValue I was curious of how this could be done. However, I prefer option 1 and let the server be more permissive and take any kind of numbers.

wevrem00:02:30

My coerce-double doesn’t take the schema into account, although it could. I just use it for a particular key that I know can come across from the front end as either a long or a double. The field is actually an array of doubles (or sometimes of longs) and the function is simply:

(defn coerce-color [info]
  (if (contains? info :item/color)
    (update info :item/color (fn [color] (mapv #(some-> % double) color)))
    info))
It could be more sophisticated by letting me specify the key (instead of hard-coding :item/color), or it could use a schema, I just haven’t had the need for that yet. I know you already know this, but one thing to note: the decoding of long or double is not something the back end controls. It is the front end that determines how to encode the value and then the back end is just reacting to whatever it is sent and decoding it. My coerce function is used defensively, but I think I’m going to experiment with being proactive and seeing if I can force js on the front-end to encode a double no matter what, and I think one way to do that is to box it up in a tagged value. I’m already having success with that with bigdecimal, so I think it could work for doubles, too.