Skip to main content

Getting Started

Pathom is a Clojure/script library to model attribute relationships.

Install

{:deps
{com.wsscode/pathom3 {:mvn/version "LATEST_VERSION"}}}
tip

Latest version: Clojars Project

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:

  1. The resolver input must be a map, so the input information is labeled.
  2. A resolver must return a map, so the output information is labeled.
  3. 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
info

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.