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?
I’m in the same boat as you guys. Amplify + re-frame + graphQL + cognito.
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 🙂
I don’t think there is a channel for that yet #amplify-whyyyy 😄
@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.
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 { ... }
I do the same as pwojnowski
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)))
@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?
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.
amplify pull
will recreate the aws-exports.js
file.
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?
@lostineverland amplify pull helped, thanks!
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”?
To setup backend part you can follow: https://serverless-stack.com/#table-of-contents
It walks you thru setting up Cognito (to auth users), Api Gateway (REST api to your lambda) and lambda.
Then you can use modules from aws-amplify, which you need.
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",
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))
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)))
Then just call AWS api methods like in JS. Just need to handle promises somehow - I used Promesa library.
@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.
@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.
Sorry just to clarify. Are you not doing all of this with clojurescript? Or do you intend to run javascript in your lambda?
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
Ok so is your question just about how to consume the API from your re-frame frontend?
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.
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?
This is the documentation: https://material-table.com/#/docs/features/remote-data
It worked. It's not fancy, but it's a workaround.
Just pass a vector of maps, don't use the promise interface.
All the fetching and remote-related stuff will be done in re-frame events/effects.
Have you had a look at the reagent interop guide? https://github.com/reagent-project/reagent/blob/master/doc/InteropWithReact.md
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.
Thanks, that's one way to deal with it
Yes, but I found nothing that helps with my case... 😢
I saw that p-himik responded on your other post. Did that help you?
I'm going to try asap and report back
I am looking at it; you are using ajax, doesn't reframe have some idiomatic way to make async requests with callbacks or promises?
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 😄
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?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 😕
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)}
...}}))
It works great!
So thanks @me1987!
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
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)
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}))
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.