re-frame

https://github.com/Day8/re-frame/blob/master/docs/README.md https://github.com/Day8/re-frame/blob/master/docs/External-Resources.md
dabrazhe 2020-06-29T13:52:00.121300Z

Hi re-framers. I've setup a re-frame project for AWS amplify. It works but it's kind of black box to me. What's the minimal viable effort i need to make to learn how to plugin the back end API (lambdas on AWS). Ie the crash course in re-frame for back end /infrastructure engineers?

lostineverland 2020-07-02T00:09:46.226700Z

I’m in the same boat as you guys. Amplify + re-frame + graphQL + cognito.

lostineverland 2020-07-02T00:11:32.226900Z

I went down a rabbit hole of go blocks for the async calls to appsync. If you guys have a channel i would love to join 🙂

Jakob Durstberger 2020-07-02T07:33:50.243800Z

I don’t think there is a channel for that yet #amplify-whyyyy 😄

dabrazhe 2020-07-02T09:48:55.244400Z

@pwojnowski @jakob.durstberger Do you know how to create aws-exports.js automatically ? I redeployed the stack in another account and the whole file is off, naturally.

pwojnowski 2020-07-02T09:51:12.244600Z

I've got whole configuration as cljs map:

(defn aws-config []
  (let [env (resolve-env)]
    (print "Current env:" env)
    {
     :API {:endpoints [{:name api-name :endpoint (format-endpoint-url env)}]}
     :Auth { ... }

Jakob Durstberger 2020-07-02T09:51:35.244800Z

I do the same as pwojnowski

pwojnowski 2020-07-02T09:52:34.245Z

Then in the code I just convert it to js:

(defn configure-amplify []
  (print "Configuring AWS Amplify.")
  (let [js-config (clj->js (config/aws-config))]
    (.configure Auth js-config)
    (.configure API js-config)))

dabrazhe 2020-07-06T11:10:31.280100Z

@pwojnowski what's the full function, how do you export it to the aws-exports.js file? I guess the file is needed for the amplify cli to deploy properly?

pwojnowski 2020-07-06T11:41:34.280300Z

There's no need to export it to the aws-exports.js file. You just pass js-config to the configure functions as above. They configure Amplify modules. AFAIR in Amplify docs there's that you can use either aws-externs.js file or configure them using these functions.

lostineverland 2020-07-06T16:48:16.280600Z

amplify pull will recreate the aws-exports.js file.

lostineverland 2020-07-06T16:59:56.280800Z

In my project I’m also making use of AppSync’s GraphQL service. Amplify creates the boilerplate code for all CRUD which I can then make use of in re-frame. The actual graphQL calls are tucked within a reg-fx call. I’m curious if this sounds appropriate to you?

dabrazhe 2020-07-09T20:22:46.382500Z

@lostineverland amplify pull helped, thanks!

👍 1
Jakob Durstberger 2020-06-29T14:01:42.121400Z

Hey, I am playing around with re-frame + AWS amplify myself. Are you using the auth part of amplify? And what do you mean with “learn how to plug the backend api”?

pwojnowski 2020-06-29T14:03:30.121700Z

To setup backend part you can follow: https://serverless-stack.com/#table-of-contents

pwojnowski 2020-06-29T14:04:25.122Z

It walks you thru setting up Cognito (to auth users), Api Gateway (REST api to your lambda) and lambda.

pwojnowski 2020-06-29T14:04:55.122200Z

Then you can use modules from aws-amplify, which you need.

pwojnowski 2020-06-29T14:06:00.122400Z

In package.json you would need sth like:

"dependencies": {
    "@aws-amplify/api": "^1.2.4",
    "@aws-amplify/auth": "^1.5.0",
    "@aws-amplify/core": "^1.2.4",

pwojnowski 2020-06-29T14:08:44.122600Z

Then in somewhere in the code (maybe your-app.core ns) you could add sth like:

(defn ^:dev/after-load start []
  (routes/app-routes)
  (services/configure-amplify)
  (views/init!))

(defn ^:export main []
  (re-frame/dispatch-sync [::events/initialize-db])
  (start))

pwojnowski 2020-06-29T14:10:06.122800Z

Where configure-amplify does sth like:

(ns theapp.services
  (:require
   ["@aws-amplify/auth" :default Auth]
   ["@aws-amplify/api" :default API]
   [re-frame.core :as re-frame]))

;; (set! (.. Amplify -Logger -LOG_LEVEL) "DEBUG")

(defn configure-amplify []
  (print "Configuring AWS Amplify.")
  (let [js-config (clj->js (config/aws-config))]
    (.configure Auth js-config)
    (.configure API js-config)))

pwojnowski 2020-06-29T14:11:25.123Z

Then just call AWS api methods like in JS. Just need to handle promises somehow - I used Promesa library.

dabrazhe 2020-06-29T15:24:13.127800Z

@jakob.durstberger I am using Cognito. I can login and display the welcome page. What i mean I to be able call and asynchronously get the call back and display the result on the page. the API will retrieve data eg from S3 and I want to display them the various results from the several calls as they arrive.

dabrazhe 2020-06-29T15:28:38.128500Z

@pwojnowski I guess I need to understand how to render the results in the page. Say I've got an endpoint that would return a clj hashmap after I parse all that json. How to i call this endpoint from re-frame and how do I display results? Some sort of hiccup library? I know very little of javascript and don't expect people to explain in slack, I'd like to know what shall I read first.

Jakob Durstberger 2020-06-29T15:45:02.129Z

Sorry just to clarify. Are you not doing all of this with clojurescript? Or do you intend to run javascript in your lambda?

dabrazhe 2020-06-29T15:46:41.129200Z

The lambdas are a separate CLJ code base and actually the tech behind them is transparent to the front end. Think of them as a common Rest/graphql endpoitns

Jakob Durstberger 2020-06-29T15:47:46.129400Z

Ok so is your question just about how to consume the API from your re-frame frontend?

Jakob Durstberger 2020-06-29T15:51:12.129700Z

I don’t know if this is any help to you. I am currently figuring a lot of this stuff out myself. I don’t properly handle the response from my API yet, but that’s how I make the call to the backend. https://gitlab.com/JDurstberger/nozuma/-/blob/master/website/src/cljs/website/dashboard/events.cljs Please be aware that I haven’t separated out any of the concerns into separate files so it’s a bit of a mess right now.

mdallastella 2020-06-29T16:13:49.130Z

Hi everyone, I am stuck with a "problem". I'm using a react component, material-table, which takes a data prop. This prop can either be a vector of maps or a function that must return a js/Promise. How can I deal with it from a re-frame point of view?

mdallastella 2020-06-29T16:17:08.130100Z

This is the documentation: https://material-table.com/#/docs/features/remote-data

mdallastella 2020-07-01T17:39:16.197100Z

It worked. It's not fancy, but it's a workaround.

p-himik 2020-06-29T16:18:47.130200Z

Just pass a vector of maps, don't use the promise interface.

p-himik 2020-06-29T16:19:41.130600Z

All the fetching and remote-related stuff will be done in re-frame events/effects.

Jakob Durstberger 2020-06-29T16:19:44.130900Z

Have you had a look at the reagent interop guide? https://github.com/reagent-project/reagent/blob/master/doc/InteropWithReact.md

p-himik 2020-06-29T16:22:39.131200Z

But that works only if you can avoid using built-in pagination controls OR if you can hook them up to custom functions that would dispatch the relevant events. If neither is the case, you can still use the promise interface, but you will have to dispatch events that accept the resolve function as a parameter. Not ideal, just at the whole promise interface, but it will work.

mdallastella 2020-06-29T16:25:23.131400Z

Thanks, that's one way to deal with it

mdallastella 2020-06-29T16:30:37.131600Z

Yes, but I found nothing that helps with my case... 😢

Jakob Durstberger 2020-06-29T16:35:53.131800Z

I saw that p-himik responded on your other post. Did that help you?

mdallastella 2020-06-29T16:40:56.132Z

I'm going to try asap and report back

dabrazhe 2020-06-29T16:57:35.132200Z

I am looking at it; you are using ajax, doesn't reframe have some idiomatic way to make async requests with callbacks or promises?

Jakob Durstberger 2020-06-29T17:08:55.132400Z

I looked into using the reframe-http-fx effect handler, but I couldn’t figure out how to combine it with amplify returning the user session in a promise. https://github.com/Day8/re-frame-http-fx As I said, still figuring a lot of that out myself 😄

kelveden 2020-06-29T18:48:58.136100Z

I think that I'm missing something obvious here but I'll ask anyway... I have an effectual handler that makes an HTTP request and I want to include a bearer token with the request. That token is available from a window.getToken javascript function - but that function returns a promise. So my handler looks something like this:

(rf/reg-event-fx
  :search
  [app-state-spec-interceptor]
  (fn [_ _]
    (-> (. js/window getToken)
        (.then (fn [token]
                 {:http-xhrio {:method          :get
                               :uri             "/some/url"
                               :headers         {"Authorization" (str "Bearer " token)}
                               :format          (ajax/json-request-format)
                               :response-format (ajax/json-response-format {:keywords? true})
                               :on-success      [:search-ready]
                               :on-failure      [:search-failed]
                               :timeout         5000}})))))
But this doesn't work as the function is returning a promise rather than a map. Does anyone have a suggestion as to how I should be doing this with re-frame?

Jakob Durstberger 2020-06-30T07:53:44.137700Z

Interesting, I have the exact same issue and just used the AJAX lib. It doesn’t feel right but the above mentioned method is also weird to me, as I’ll need that token for a lot of calls 😕

kelveden 2020-07-01T15:21:27.195300Z

In the end I actually wrapped up the defining of the two "chained" handlers into a separate function:

(rf/reg-fx
  :get-token
  (fn [{:keys [dispatch args]}]
    (-> (.getToken js/window)
        (.then #(let [dispatch-args (vec (concat [dispatch %] args))]
                  (rf/dispatch dispatch-args))))))

(defn reg-token-event-fx
  ([id interceptors handler]
   (let [with-token-id (keyword (str (name id) "-with-token"))]
     (rf/reg-event-fx with-token-id handler)
     (rf/reg-event-fx
       id
       interceptors
       (fn [_ [_ & args]]
         (cond-> {:get-token {:dispatch with-token-id}}
                 (not (empty? args)) (assoc-in [:get-token :args] args))))))
  ([id handler]
   (reg-token-event-fx id nil handler)))
then a handler definition looks something like this:
(reg-token-event-fx
  :search
  [app-state-spec-interceptor]
  (fn [{{:keys [search-text]} :db} [_ token]]
    {:http-xhrio {:uri     (str "/search?q=" search-text)
                  :headers {"Authorization" (str "Bearer " token)}
                  ...}}))

kelveden 2020-07-01T15:21:42.195500Z

It works great!

kelveden 2020-07-01T15:22:04.195700Z

So thanks @me1987!

dabrazhe 2020-06-29T19:37:33.136400Z

This -fx looks promising. Actually the user session is returned by congito and I did not have to do much about it. I used this guide to set ip up https://github.com/omnyway-labs/amplify_cljs_example

chucklehead 2020-06-29T19:41:03.136700Z

it may not matter for what you're doing, but just fyi, that example uses the legacy Amplify react UI components (aws-amplify-react instead of @aws-amplify/ui-react)

1
whoops 2020-06-29T23:12:25.136900Z

Honestly, I kind of wish co-fx could be async specifically for situations like this. But they can't, so what you have to so instead is fine a second event. Going all in on the re-frame way it'd look something like:

(rf/reg-fx
 ::get-token
 [to-dispatch]
 (-> (.getToken js/window)
     (.then #(rf/dispatch [::got-token %]))))

(rf/reg-event-fx
 ::got-token
 (fn [_ [_ token]]
   {:http-xhrio {:method          :get
                 :uri             "/some/url"
                 :headers         {"Authorization" (str "Bearer " token)}
                 :format          (ajax/json-request-format)
                 :response-format (ajax/json-response-format {:keywords? true})
                 :on-success      [:search-ready]
                 :on-failure      [:search-failed]
                 :timeout         5000}}))


(rf/reg-event-fx
  :search
  [app-state-spec-interceptor]
  (fn [_ _]
    {::get-token ::got-token}))

kelveden 2020-06-29T23:26:31.137200Z

Thanks! I'll give that a go. I've not read about that method of chaining events together: I'll have to dig into the docs again.