This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-03-11
Channels
- # announcements (1)
- # aws (5)
- # beginners (35)
- # calva (18)
- # clerk (5)
- # clojure (20)
- # clojure-berlin (1)
- # clojure-dev (12)
- # clojure-europe (16)
- # clojure-nl (1)
- # clojure-norway (159)
- # clojure-uk (5)
- # clojurescript (8)
- # conjure (1)
- # cursive (18)
- # events (10)
- # fulcro (23)
- # hyperfiddle (5)
- # introduce-yourself (3)
- # juxt (2)
- # off-topic (1)
- # polylith (4)
- # portal (11)
- # releases (1)
- # shadow-cljs (4)
- # xtdb (9)
- # yamlscript (1)
Hey there, What is the recommended way of sending REST requests to some endpoints (not Pathom, just normal endpoints) and retrieving responses the Fulcro-way, i.e. doing so via transactions and inserting the response's data into the local state? There's a section on creating a custom remote in the book but tbh I'm a bit lost here. Any help or advice would be highly appreciated. Thank you.
The recommend way is to make a new remote. The pluggable middleware of http-remote will let you do what you want. Mainly, you need to convert the request from Fulcro into the GET request (at the middleware layer) and the rewrite the response so it matches the naming/shape of the query you’re using for normalization. For example, here’s some middleware for making a GET request against a CDN that holds EDN files that already have the shape I want:
(defn handle-get-response [{:keys [status-code original-transaction body] :as resp}]
(let [k (some-> original-transaction eql/query->ast1 :dispatch-key)]
(if (= 200 status-code)
(assoc resp :body {k (edn/read-string body)})
(assoc resp :body {}))))
(defn handle-get-request [{:keys [url body] :as req}]
(if-let [url (some-> body eql/query->ast1 :params :url)]
(assoc req :url url :method :get :body "" :response-type "text")
(throw (ex-info "Invalid GET request. No URL" {:body body}))))
(def get-response-mw (fn [resp] (handle-get-response resp)))
(def get-request-mw (fn [req] (handle-get-request req)))
(net/fulcro-http-remote {:url "ignored"
:response-middleware get-response-mw
:request-middleware get-request-mw})
so I just put that in :remotes option as :get
, and then issue loads like this:
(df/load! @app-atom :all-interests Interest
{:remote :get
:params {:url ""}})
and in the EDN file I’ve got a vector of maps that have the k/v pairs for the query of Interestso if it was a REST endpoint, you’d need to convert the JSON to EDN and rename the keys
If it were me, I’d follow the RAD modelling. I’d make up options for the RAD attributes that specify the incoming JSON name of the key, then you can automate the middleware using that model to know how to rename the keys as they come in.
But in structure at least, you just need to see that submitting an EQL query like:
[{:all-interests [:interest/name :interest/category]}]
whose response from the GET is:
[{"name": "foo", "category": "bar"}, ...]
needs to be transformed into:
{:all-interests [{:interest/name "foo" :interest/category "bar"} ...]}
by the middleware, and then all of the mechanisms of Fulcro will work.Notice that Pathom 3 can be run in the browser and I believe has some nice integration with Rest.
I see. Thanks for the help. I think I'll create a custom remote to handle this. RAD is a bit foreign to me given that I'm not that well-versed in Fulcro to begin with to understand what it's doing. Running Pathom 3 in CLJS is interesting, but it does seem like it's similar to creating a custom remote, i.e. I still have to write code to handle the conversion, the difference is just where to do so.
@U0CKQ19AQ Ok so after trying it out for a bit, I'm seeing some weird behaviour that I don't fully know why it's happening. I have a remote:
:identity (net/fulcro-http-remote {:url IDENTITY_URL
:response-middleware get-response-mw
:request-middleware get-request-mw})
where the middlewares are:
(defn handle-get-response [{:keys [status-code original-transaction body] :as resp}]
(assoc resp :body {}))
(defn handle-get-request [{:keys [url body] :as req}]
(let [body (some-> body eql/query->ast1 :params :body)]
(assoc req :url url :method :post :body body :content-type "application/json")))
(def get-response-mw (fn [resp] (handle-get-response resp)))
(def get-request-mw (fn [req] (handle-get-request req)))
On the server side, this is a simple server:
(ring/ring-handler
(ring/router
["/identity" {:post {:middleware [#(wrap-defaults % defaults-config-native)]
:handler identity-handler}}]))
where the identity-handler
is
(defn identity-handler
[{:keys [body] :as req}]
{:status 200
:headers {"Content-Type" "application/json"}
:body "coolbean"})
The load call (df/load! this :cool Root {:remote :identity :params {:body {:cool "bean"}}})
does produce an HTTP request and send to the server correctly. However, the body is always nil
on the server side.
Printing out the outgoing request and Fulcro was telling me that it does have {:cool "bean"}
as the body, so somewhere along the line the body got nuked but I'm not sure where exactly.
Am I missing anything here?1. You’re using the url of the req, not the prams…so just IDENTITY_URL will be used
2. Where are you converting body
to actual JSON?
at least those 2 things look wrong to me
1. The params dont have url as I've set it up as a new remote. 2. I dont have that atm. So the conversion has to manual? In which case, is it a JSON string or something else?
JSON string…look at the implementation of http-remote. You can control everything about the low-level request, but it is just using google’s xhrio. So yes, if YOU say content type is JSON, then body has to be a JSON string.
response types are controllable in terms of low level types, see line 109 in http-remote:
(def response-types {:default ""
:array-buffer "arraybuffer"
:blob "blob"
:document "document"
:text "text"})
you have to understand the low-level env you’re in, which is js/browser. The remote IS the interfacing between the Fulcro/Clojurescript world and the “rest of the world”
same with the response…you have to make sure to convert it to EDN, namespace the kws, and put it in the right shape for the query. In your load
case, you need to put EDN like this in the resp body:
{:cool {kvs that Root asked for}}