This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-04-27
Channels
- # announcements (9)
- # aws (1)
- # aws-lambda (12)
- # babashka (18)
- # beginners (37)
- # calva (27)
- # clerk (15)
- # clojure (16)
- # clojure-conj (13)
- # clojure-europe (44)
- # clojure-germany (3)
- # clojure-norway (27)
- # clojure-uk (1)
- # cursive (6)
- # data-science (24)
- # datahike (7)
- # datomic (40)
- # fulcro (5)
- # hoplon (33)
- # hyperfiddle (9)
- # introduce-yourself (6)
- # jobs (1)
- # lsp (22)
- # nbb (2)
- # off-topic (15)
- # pathom (37)
- # pedestal (3)
- # polylith (7)
- # portal (1)
- # re-frame (7)
- # releases (1)
- # remote-jobs (1)
- # rewrite-clj (6)
- # sci (1)
- # scittle (1)
- # xtdb (7)
When using the pathom3 boundary interface is there a way to tell it to only return attributes that were explicitly queried?
I’ll try using com.wsscode.pathom3.format.eql/map-select
, just looking to reduce what’s sent over the wire
hi Tom, not sure what you mean, the boundary interface should only return attributes explicitly queried, example:
(ns demos.boundary
(:require [com.wsscode.pathom3.connect.indexes :as pci]
[com.wsscode.pathom3.connect.operation :as pco]
[com.wsscode.pathom3.interface.eql :as p.eql]))
(pco/defresolver user-data []
{:user/id 123
:user/name "foo"
:user/email ""})
(def env
(-> {}
(pci/register [user-data])))
(def request (p.eql/boundary-interface env))
(comment
(request [:user/name]))
=> {:user/name "foo"}
can you show what is the case you see where the output doesn't match the query?
sure, so I tried to make a minimal repro using just the boundary interface as above and it’s working as expected so it must be something odd we’re doing we’re returning the result from the boundary interface as part of a pedestal handler like so:
(def handle-post
(handler
::pathom
{:summary "Pathom handler"}
(fn [{:keys [body-params] :as request}]
{:status 200
:body ((p.eql/boundary-interface
(-> (pci/register {::p.error/lenient-mode? true} registry)
(merge request)))
body-params)})))
I also suggest you cache the boundary interface creation, re-building it on every request is suboptimal (you keep paying the price to register indexes and a few things, not huge deal, but something you can easly optimize)
re: caching the interface, would we still be able to inject things into the env on each request?
yes you can, the boundary interface has an arity 2 option where you can use the first argument to extend env
the extension can be in 2 forms: • a map (that will merge with env) • a fn, that will receive the env and must return some updated env
I need to head off but I’ll take a crack at making that repro tomorrow morning 🙂
We did fix our issue by wrapping the result of the boundary interface in map-select
, using the same query, and it resulted in a massively smaller return value so something fishy going on..
I would check if you have some plugins that might be doing something, because under the hood boundary interface uses the same eql/process
/ eql/process-ast
, which do already apply map-select
at the end
So it looks like the issue was that some of our resolvers actually return https://cljdoc.org/d/toucan/toucan/1.15.4/doc/toucan-db-functions#classes-of-fetched-objects rather than plain maps and map-select-ast
returns them as-is (because of a coll/native-map?
check). I forgot we needed to walk the return value from the boundary interface with (clojure.walk/prewalk #(if (record? %) (into {} %) %)
to get map-select
to work, that should have been a hint as to why it wasn’t being trimmed in the first place 😆
cool, glad to hear, one idea, you can try to use ::p.f.eql/wrap-map-select-entry
plugin entry and apply your record to map
thing there, this way you avoid having to do an extra walk across the structure
oh, I just looked at the code, seems like unfortunally the native-map check happens before the plugin call, so maybe not
ah I see what you mean, the p.plugin/run-with-plugins env ::wrap-map-select-entry
part
yup, it might still work, give it a try
because it wraps the entry of each map, so unless your root map is a toucan
, it may work
hmm doesn’t seem to be working, is this the correct way to get the value of each node?
(defn no-more-records-wrapper [mse]
(fn [env source {k :key :as ast}]
(if (and (contains? source k)
(record? (get source k)))
(let [v (get source k)]
(tap> ['record!! v])
(medley/map-entry k (into {} v)))
(mse env source ast))))
(p.plugin/defplugin no-more-records-plugin
{::p.f.eql/wrap-map-select-entry no-more-records-wrapper})
gotcha, I was trying to repro your case, but got surprised that this worked fine:
(ns demos.boundary
(:require [clojure.walk :as walk]
[com.wsscode.pathom3.connect.indexes :as pci]
[com.wsscode.pathom3.connect.operation :as pco]
[com.wsscode.pathom3.interface.eql :as p.eql]
[com.wsscode.pathom3.plugin :as p.plugin]))
(defrecord SampleRec [])
(pco/defresolver user-data []
{::pco/output [:user/id :user/name :user/email]}
(-> (->SampleRec)
(assoc
:user/id 123
:user/name "foo"
:user/email "")))
(def env
(-> {}
(pci/register [user-data])))
(def request (p.eql/boundary-interface env))
(comment
(request [:user/name]))
=> {:user/name "foo"}
anyway, I think this plugin should work:
(p.plugin/defplugin no-more-records-plugin
{:com.wsscode.pathom3.connect.runner/wrap-resolve
(fn [resolve]
(fn [env input]
(let [res (resolve env input)]
(walk/prewalk
#(if (record? %)
(do
(println "WARN: some resolver returned a record")
(into {} %)) %)
res))))})
ideally you should avoid this on prod, and make sure you only give pathom native maps, so the warn can help during dev
very curious about the repro, it could be that we’re returning collections of records?
could be, let me try
also, a little update to make the plugin more informative:
(p.plugin/defplugin no-more-records-plugin
{:com.wsscode.pathom3.connect.runner/wrap-resolve
(fn [resolve]
(fn [env input]
(let [res (resolve env input)]
(walk/prewalk
#(if (record? %)
(let [node (-> env :com.wsscode.pathom3.connect.planner/node)]
(println (str "WARN: " (::pco/op-name node) " resolver returned a record"))
(into {} %))
%)
res))))})
(ns demos.boundary
(:require [clojure.walk :as walk]
[com.wsscode.pathom3.connect.indexes :as pci]
[com.wsscode.pathom3.connect.operation :as pco]
[com.wsscode.pathom3.interface.eql :as p.eql]
[com.wsscode.pathom3.plugin :as p.plugin]))
(defrecord SampleRec [])
(pco/defresolver user-data []
{::pco/output
[{:users
[:user/id :user/name :user/email]}]}
{:users
[(-> (->SampleRec)
(assoc
:user/id 123
:user/name "foo"
:user/email ""))]})
(p.plugin/defplugin no-more-records-plugin
{:com.wsscode.pathom3.connect.runner/wrap-resolve
(fn [resolve]
(fn [env input]
(let [res (resolve env input)]
(walk/prewalk
#(if (record? %)
(let [node (-> env :com.wsscode.pathom3.connect.planner/node)]
(println (str "WARN: " (::pco/op-name node) " resolver returned a record"))
(into {} %))
%)
res))))})
(def env
(-> {}
(pci/register [user-data])
(p.plugin/register no-more-records-plugin)))
(def request (p.eql/boundary-interface env))
(comment
(request [{:users [:user/name]}]))
anyway, I think this plugin should work: