Getting Started
Pathom is a Clojure/script library to model attribute relationships.
Install
{:deps
{com.wsscode/pathom3 {:mvn/version "LATEST_VERSION"}}}
Namespace aliases
When you read code from the examples in this documentation they will use alias to reference namespaces, here you can find what the aliases point to:
; main namespaces, you are very likely to use at least these
[com.wsscode.pathom3.connect.indexes :as pci]
[com.wsscode.pathom3.connect.operation :as pco]
[com.wsscode.pathom3.interface.eql :as p.eql]
; now some common namespaces to use
[com.wsscode.pathom3.connect.built-in.resolvers :as pbir]
[com.wsscode.pathom3.connect.built-in.plugins :as pbip]
[com.wsscode.pathom3.interface.async.eql :as p.a.eql]
[com.wsscode.pathom3.interface.smart-map :as psm]
[com.wsscode.pathom3.plugin :as p.plugin]
; now the least common used
[com.wsscode.pathom3.cache :as p.cache]
[com.wsscode.pathom3.connect.foreign :as pcf]
[com.wsscode.pathom3.connect.operation.transit :as pcot]
[com.wsscode.pathom3.connect.planner :as pcp]
[com.wsscode.pathom3.connect.runner :as pcr]
[com.wsscode.pathom3.error :as p.error]
[com.wsscode.pathom3.format.eql :as pf.eql]
[com.wsscode.pathom3.path :as p.path]
General attribute modeling view
Attribute modeling is a way to think and design information systems.
Consider the question: What's the temperature in the city of Recife now?
Using my attribute modeler hat, I can break this description into two attributes and one
value. The demand is for a :temperature
, and the available information is the
:city
with the value of Recife
. Or we can express it as a Clojure map:
{:city "Recife"
:temperature ?}
Note we left ?
for the unknown attributes. In a way, this map expresses the same thing
as the question we had before.
Now, to realize the :temperature
from the city
, we write a resolver
establishing this relationship.
; our mock temperatures database
(def temperatures
{"Recife" 23})
(pco/defresolver temperature-from-city [{:keys [city]}]
{:temperature (get temperatures city)})
A resolver looks like a function but with some constraints:
- The resolver input must be a map, so the input information is labeled.
- A resolver must return a map, so the output information is labeled.
- A resolver may also receive another map, containing the environment information.
The next step is to create the indexes:
(def indexes
(pci/register [temperature-from-city]))
Pathom uses the indexes to traverse the attribute relationships.
Now to make the original question to Pathom, we are going to use the Smart Map interface:
; this creates a smart map, using our indexes, and with some initial data
(def smart-map (psm/smart-map indexes {:city "Recife"}))
; smart maps work as regular maps when looking for the initial data
(:city smart-map); => "Recife"
; but the difference comes when we ask for keys not present in the map, but reachable
; via resolvers
(:temperature smart-map); => 23
Note that in this example, we only mention the indexes and the attributes, the resolver names get abstracted from the user and left for Pathom to figure when to call then.
Now consider a slightly different question: Is it cold in Recife
now?
{:city "Recife"
:cold? ?}
To compute the :cold?
attribute, we can write a resolver that depends on the :temperature
attribute:
(pco/defresolver cold? [{:keys [temperature]}]
{:cold? (< temperature 20)})
Update the indexes definition to also include the cold?
resolver:
(def indexes
(pci/register [temperature-from-city cold?]))
Now, back to the question: Is it cold in Recife
now? This time we will use a different
interface, the EQL interface.
; this time we will use the EQL interface to trigger the Pathom engine
(p.eql/process
indexes
{:city "Recife"}
[:cold?])
; => {:cold? false}
Note that our "lookup code" remains the same size, although we have to run more operations now. This ability to abstract the function call chain is the primary utility you should look for in Pathom.
In this getting started, we talked a bit about attribute modeling, how to write a few resolvers and how to trigger the Pathom engine to fulfill attribute questions.
You can find a complete tutorial playing with weather API's at the Pathom Tutorial page.
Core Concepts
Here is a list of the main concepts in Pathom that can help you navigate the documentation.
Resolver
Resolvers are building blocks that establish relationships between attributes. Pathom secret sauce is the ability to traverse a graph of attribute relationships, defined by resolvers, given some user request.
Environment
Is a map containing all the necessary context required to process a request. Data for Pathom internals uses Pathom-specific namespaced keys, and you will typically add your own entries (database connections, etc.) such that all these data are available to your resolvers.
Planner
Given the environment, some initial data and a request, the planner is responsible for figuring what resolvers to call, in what order.
Runner
Once the plan is ready the runner executes the plan, by traversing and executing resolvers, then responses are coming as expected and chooses the path when multiple options shaping the results to satisfy the request. It handles errors as well as potential conflicts are available.(e.g. multiple resolvers can handle a given request).
Interface
To bundle the process together, you interact using one of the Pathom interfaces:
- EQL is a syntax that allows users to express a data shape, and then Pathom fill the requirements using the logic from resolvers.
- Smart Maps is a custom map data structure, similar to a Datomic Entity, but instead of loading from a database, realized using resolvers.