Foreign Environments
Foreign parsers is a sub-system of Dynamic Resolvers, which are still in research/experimental stages.
Foreign Pathom is the ability of Pathom 3 to make automatic integration between different Pathom environments.
This mechanism provides a simple way to connect resolvers that may run in completely different contexts (different machines or processes).
Foreign setup
To demonstrate it working, let's start with some resolvers to list some TODO ids and another resolver to pull the TODO details.
(ns com.wsscode.pathom3.docs.demos.core.foreign
(:require [com.wsscode.pathom3.connect.operation :as pco]
[com.wsscode.pathom3.connect.indexes :as pci]
[com.wsscode.pathom3.interface.eql :as p.eql]
[com.wsscode.pathom3.connect.foreign :as pcf]))
(def todo-db
{1 {:todo/id 1
:todo/title "Write foreign docs"
:todo/done? true}
2 {:todo/id 2
:todo/title "Integrate the whole internet"
:todo/done? false}})
(pco/defresolver todo-items []
{::pco/output
[{:app/all-todos
[:todo/id]}]}
; export only the ids to force the usage of another resolver for
; the details
{:app/all-todos
[{:todo/id 1}
{:todo/id 2}]})
(pco/defresolver todo-by-id [{:todo/keys [id]}]
{::pco/output
[:todo/id
:todo/title
:todo/done?]}
(get todo-db id))
(def foreign-env
(pci/register
{:com.wsscode.pathom3.connect.planner/plan-cache* (atom {})}
[todo-items
todo-by-id]))
(def foreign-request
(p.eql/boundary-interface foreign-env))
(comment
(foreign-request
[{:app/all-todos
[:todo/title
:todo/done?]}]))
Now we are going to start a new Pathom environment, connect and extend the previous one. The local extension will add the ability to know if a TODO item was cancelled. You can imagine this is a separate service responsible for the cancellation part of this hypothetical system.
(def canceled-todos
#{2})
(pco/defresolver todo-canceled? [{:todo/keys [id]}]
{:todo/cancelled? (contains? canceled-todos id)})
(def local-env
(pci/register
{:com.wsscode.pathom3.connect.planner/plan-cache* (atom {})}
[; pull the remote instance
(pcf/foreign-register foreign-request)
; add our custom resolver on top
todo-canceled?]))
Using the boundary interface ensures the request handler fulfills the expected contract when calling a remote Pathom instance.
How it works
When we use (pcf/foreign-register foreign-request)
, it makes a request that asks
for the indexes available in that instance.
You can try it yourself by requesting the query:
[:com.wsscode.pathom3.connect.indexes/indexes]
Pathom them transform the indexes in dynamic resolvers and
we add these new indexes using pci/register
.
Check the dynamic resolvers documentation for more details about this process.
Foreign HTTP Example
It's important to note that the foreign environment might well be in a different machine. The integration process is serializable, and can work over the network.
I have this same example deployed at https://southamerica-east1-pathomdemos.cloudfunctions.net/development-pathom-server-demo
Here is how we can do this integration using this HTTP based communication:
(ns com.wsscode.pathom3.docs.demos.core.foreign
(:require
[com.wsscode.pathom3.connect.foreign :as pcf]
[com.wsscode.pathom3.connect.indexes :as pci]
[com.wsscode.pathom3.connect.operation :as pco]
[com.wsscode.pathom3.connect.operation.transit :as pcot]
[com.wsscode.pathom3.interface.eql :as p.eql]
[com.wsscode.transito :as transito]
[org.httpkit.client :as http]))
(defn tread [s]
(transito/read-str s {:handlers pcot/read-handlers}))
(defn twrite [x]
(transito/write-str x {:handlers pcot/write-handlers}))
; helper to deal with encoding, sending via HTTP and decoding
(defn request-remote-pathom [url request]
(-> @(http/request
{:url url
:method :post
:headers {"Content-Type" "application/transit+json"
"Accept" "application/transit+json"}
:body (twrite request)})
:body
tread))
(def canceled-todos
#{2})
(pco/defresolver todo-canceled? [{:todo/keys [id]}]
{:todo/cancelled? (contains? canceled-todos id)})
(def url "https://southamerica-east1-pathomdemos.cloudfunctions.net/development-pathom-server-demo")
(def env
(pci/register
{:com.wsscode.pathom3.connect.planner/plan-cache* (atom {})}
[; pull the remote instance
(pcf/foreign-register
#(request-remote-pathom url %))
; add our custom resolver on top
todo-canceled?]))
(comment
(p.eql/process
env
[{:app/all-todos
[:todo/title
:todo/done?
:todo/cancelled?]}]))