Fork me on GitHub

is there any way to use pathom parameters in a component query without disabling the sanity checks? e.g. I'm using this query:

[{'(:tokens {:sort :token/id}) (comp/get-query Token)}]
and getting this error:
Encountered error when macroexpanding com.fulcrologic.fulcro.components/defsc.
defsc Gallery: [tokens] was destructured in props, but does not appear in the :query! at line 163 app/ui/root.cljs
this was my original query that doesn't error:
[{:tokens (comp/get-query Token)}]


use a fn that returns a query, this will disable the fulcro validation


thanks, that works for me


Yeah, I need to fix the macro so that it uses the AST of the query...that's pretty old code, but there's always been that workaround.


My first stab at publishing my “toy” RSS archive reader, built upon Fulcro RAD Demo — this is the first time I’ve published anything like this, and I may be violating a bunch of norms. Any comments or suggestions appreciated. And thanks to @tony.kay and @holyjak and all y’all.

🙌 12

I’m still working on a blog post that describes what it’s been like learning Fulcro. But this evening, taking what I’ve learned, I started writing taking a stab at what my Trello card manager rewrite might look like. In an hour, I got my first two Pathom resolvers written, and my first screen showing all the Trello boards and a filter box. I’m shocked, absolutely shocked, at how quickly it came together. There is something amazing about how Fulcro encourages you to write resolvers that put data somewhere (ident) with a data shape (query), and then you write your defsc components that defines those locations (idents) and shapes (query), and you have everything at hand to display it.

awesome 2

great to hear @genekim! I'm still at the point in my journey where it takes me a while to figure out how to do simple things. but seeing the light at the tunnel 🙂

Jakub Holý (HolyJak)08:07:52

If you have any feedback on the (usefulness of) the Fulcro Minimalist Tutorial and exercises, pls let me know!


I completed the tutorial last week and it was extremely helpful. Thanks for writing it! Not sure if it's within the scope of the tutorial, but couple suggestions for more exercises: • writing pathom resolvers and putting them together • integrating with a real db (e.g. datomic or datahike) • loading data into a more deeply nested component

❤️ 2

come to think of it, I had a question regarding pathom- should sorting and filtering be done in the resolver? or is that fulcro's job on the frontend?

Björn Ebbinghaus10:07:27

@U066TMAKS It depends. When you have a small set of items, you can sort and filter in the frontend.... But if you have a large list, you probably don't want to load everything, so filter/sort has to be done by the backend, maybe even by the database.

👍 2

makes sense, thank you

Björn Ebbinghaus11:07:12

At first I had components like this:

(defsc MyList [_ {:keys [items sort-key]}] 
  {:query [:items :sort-key]}
  (->> items
    (sort-by sort-key)
    (mapv ui-item)))
Where the actual sorting is done in the render function. This works, when you have a fixed number of elements. It is nice, because you don't have to worry about items getting out of order, when you add an item to the end of :items. But eventually I want to change over to a more explicit sort:
(defmutation sort-list-by [{:keys [list/id sort-key]}
  (action [{:keys [state]}]
    ;; When your Application grows, you can add loads, other mutations, whatever... here. 
    (swap! state update-in [:list/id id :items] #(sort-by sort-key %))))

(defsc MyList2 [_ {:keys [items]}] 
  {:query [:list/id :items]
   :ident :list/id}
  (mapv ui-item items))


that's super helpful, thank you - I had my component like your first snippet. makes sense to have it in a local mutation. guessing filtering would be a local mutation in your example as well. so let's say you wanted to move the sorting/filtering to the server, would it make sense to make the sort/filter options a pathom query parameter, and do the actual sorting and filtering in the resolver?

Björn Ebbinghaus11:07:23

Well, when you don't want to load every item, you have to do it on the server. 🙂 The client would have a mutation like this:

(defsc Item ...)

(defmutation sort-list-by [{:keys [list/id sort-key]}
  (action [{:keys [app]}]
    (df/load! app :my-huge-list-of-things Item
       {:params {:limit 15 :sort-key sort-key}
        :target [:list/id id :items]}))
Actually in this case you would have a single mutation for sort/filter/limit/offset... Because you want that to happen in a single load.


ah gotcha, you put the load! in the mutation, of course

Björn Ebbinghaus11:07:34

Well, you don't have to, when the only thing you are doing is a load! like this. But this way you can grow out your mutation, and often you want to do some other things beside loading. Actually, there is a pagination example in the book: It has this mutation:

(m/defmutation goto-page [{:keys [page-number]}]
  (action [{:keys [state] :as env}]
    (load-if-missing! env page-number)
    (swap! state (fn [s]
                   (-> s
                     (init-page page-number)
                     (set-current-page page-number)
                     (gc-distant-pages page-number))))))

Björn Ebbinghaus11:07:30

You can see that it does conditional loading, garbage collection, setting of ui elements ...


ok thanks, that largely makes sense to me. what I'm missing is how to write the server side of the query. is there a resolver involved? the sample code just generates dummy items on the fly:

(defmethod api/server-read :paginate/items [env k {:keys [start end]}]
  (when (> 1000 (- end start)) ; ensure the server doesn't die if the client does something like use NaN for end
    {:value (vec (for [id (range start end)]
                   {:item/id id}))})) 


(I'm using datomic btw if that matters)


guess where I'm getting tripped up is when I should write a resolver vs when I can just directly query for data and return it, like in this api/server-read example

Björn Ebbinghaus11:07:48

You write pathom resolvers for everything. There is some non-pathom resolver code in the books, but that's mostly old stuff.

Björn Ebbinghaus12:07:12

(pc/defresolver demo-list [{:keys [db] :as env} _]
  {::pc/output [{:my-huge-list-of-things [:item/id}]}
  (let [{:keys [limit sort-key]} (-> env :ast :params)] ; 
     (get-list-from-db db {:limit limit :sort-key sort-key})}))
How you implement get-list-from-db is entirely up to you.
(defn get-list-from-db [db {:keys [limit offset sort-key]}]
  (->> db
    get-all-items ; some fn that does d/q 
    (sort-by sort-key)
    (drop offset)
    (take limit)))
But that is really up to you, your datamodel, your requirements, your database


aha, the resolver code was the part I didn't get before (specifically to get the params via (-> env :ast :params)), but this clears things up a lot -- thanks! get-list-from-db I know how to write, ideally leveraging db querying capabilities rather than operating on the entire result set, but I won't need that for a while

Jakub Holý (HolyJak)16:07:56

Thanks for the exercises ideas, @UCVND577S! Btw it is worth studying (and hopefully not too difficult) the code in fulcro-rad report ns for how reports to sorting, filtering (on the client side, I believe). But as Bjørn says, it depends. (sorry, lacking o with the dots here :))


@tony.kay I've been fighting with the CLJ remotes you mentioned. No luck yet. You said that it has been exampled in some places of the book. Could you please help me find it?

Björn Ebbinghaus15:07:30

He mentioned, how the book itself was implemented. The examples in the book make "remote" loads and mutations, but in reality, the "remote" is running in the client as well. Pathom, for example, has a remote, where the pathom parse actually runs in the client. ( Here is the chapter about writing your own remote.

tony.kay15:07:28 is the usage that creates "remotes" for the book (in cljs), and the mock remote supplied in Fulcro (which should be CLJC, now that I'm looking at it): Then also what @U4VT24ZM3 just said 😄


The simplest remote just pulls the AST from the transmit node, turns it into EDN, and runs it through a pathom processing/parser step and returns the value.


(by calling the result-handler)