-
-
Notifications
You must be signed in to change notification settings - Fork 62
Connect
Alpha - this feature is active development and the API might change.
Connect is high level reader on pathom, it providers an abstraction around data-fetching that enables declarative attribute mapping. I have an easier time explaining though examples, so let's start with one:
(ns pathom-docs.connect-hello
(:require [com.wsscode.pathom.core :as p]
[com.wsscode.pathom.connect :as p.connect]))
(def artists
{1 {:artist/id 1
:artist/name "Shpongle"
:artist/genre "psychill"}
2 {:artist/id 2
:artist/name "The Beatles"
:artist/genre "rock"}})
; this is the resolver function, using the params + env it
; should return a map proving the declared keys
(defn artist-by-id [env {:artist/keys [id]}]
(get artists id))
; create the indexes for Connect
(def indexes
(-> {}
; here we add our resolver to the indexes, the resolver must have a symbol with the fn
; and optional input, and an output containg the keys that are provided by this resolver
(p.connect/add `artist-by-id
#::p.connect {:input #{:artist/id}
:output [:artist/id :artist/name :artist/genre]})))
(def parser
(p/parser {::p/plugins [(p/env-plugin {::p/reader [p/map-reader
p.connect/all-readers]
; set the indexes
::p.connect/indexes indexes})]}))
(parser {} [{[:artist/id 1] [:artist/name]}])
; => {[:artist/id 1] #:artist{:name "Shpongle"}}
In connect, you define how inputs relate to outputs in terms of attributes. In previous example, a good way to read our resolver is: Given I have the key :artist/id
, I can provide you the keys :artist/id
, :artist/name
and artist/genre
. When you add a resolver that only needs a single input, it is indexes as an ident
, meaning you can use an ident
query to start from that attribute (as we did in the previous example).
Let's expand this example a little more, in a way to add an index that given an :artist/name
it can provide us with the :artist/id
:
(ns pathom-docs.connect-hello2
(:require [com.wsscode.pathom.core :as p]
[com.wsscode.pathom.connect :as p.connect]))
(def artists
{1 {:artist/id 1
:artist/name "Shpongle"
:artist/genre "psychill"}
2 {:artist/id 2
:artist/name "The Beatles"
:artist/genre "rock"}})
; our new index
(def artist-name->id
{"Shpongle" 1
"The Beatles" 2})
(defn artist-by-id [_ {:artist/keys [id]}]
(get artists id))
; the function that does the resolving, remember the resolves MUST return
; maps, always
(defn artist-name-to-id [_ {:artist/keys [name]}]
{:artist/id (artist-name->id name)})
; create the indexes for Connect
(def indexes
(-> {}
(p.connect/add `artist-by-id
#::p.connect {:input #{:artist/id}
:output [:artist/id :artist/name :artist/genre]})
; adding our new resolve, has a name as input and an id as output
(p.connect/add `artist-name-to-id
#::p.connect {:input #{:artist/name}
:output [:artist/id]})))
(def parser
(p/parser {::p/plugins [(p/env-plugin {::p/reader [p/map-reader
p.connect/all-readers]
; set the indexes
::p.connect/indexes indexes})]}))
(parser {} [{[:artist/name "The Beatles"] [:artist/genre]}])
; => {[:artist/name "The Beatles"] #:artist{:genre "rock"}}
Connect is able to follow the dependencies to get the information. To understand how that works, let's take a look at the indexes
:
{:com.wsscode.pathom.connect/index-fio
{pathom-docs.connect-hello2/artist-by-id
{:com.wsscode.pathom.connect/input #{:artist/id}
:com.wsscode.pathom.connect/output [:artist/id :artist/name :artist/genre]}
pathom-docs.connect-hello2/artist-name-to-id
{:com.wsscode.pathom.connect/input #{:artist/name}
:com.wsscode.pathom.connect/output [:artist/id]}}
:com.wsscode.pathom.connect/index-io
{#{:artist/id} {:artist/id {} :artist/name {} :artist/genre {}}
#{:artist/name} {:artist/id {}}}
:com.wsscode.pathom.connect/idents
#{:artist/name :artist/id}
:com.wsscode.pathom.connect/index-oif
{:artist/name {#{:artist/id} #{pathom-docs.connect-hello2/artist-by-id}}
:artist/genre {#{:artist/id} #{pathom-docs.connect-hello2/artist-by-id}}
:artist/id {#{:artist/name} #{pathom-docs.connect-hello2/artist-name-to-id}}}}
As we start the query with an ident join [:artist/name "The Beatles"]
, Connect will look at the :com.wsscode.pathom.connect/idents
key and notice it matches with one of the items. This will make it accept the ident and set the context for the sub-query, the context will be {:artist/name "The Beatles"}
.
Then, running the query, it looks up at the requests key (:artist/genre
in this case) in the index :com.wsscode.pathom.connect/index-oif
(oif
stands for: output -> input -> function). Since :artist/genre
is present on that index, it will check what input that requires, and on this case it is the #{:artist/id}
, meaning we need the id
to get the genre
. Since we don't have the :artist/id
, we go back on the index and try to look that attribute up. And look, we can get the :artist/id
if we have the :artist/name
; and that one we got! So by resolving it this way we can find a possible path given the attributes/context we have.
So far we have dealt with resolution on a single entity, now let's see how to go about joins: