### Install Pathom 3 Clojure Dependency Source: https://pathom3.wsscode.com/docs/index This code snippet demonstrates how to add Pathom 3 as a dependency in a Clojure project's `deps.edn` configuration. It specifies the `com.wsscode/pathom3` library and indicates where to place the version string. ```Clojure {:deps {com.wsscode/pathom3 {:mvn/version "LATEST_VERSION"}}} ``` -------------------------------- ### Pathom Core Concepts Overview Source: https://pathom3.wsscode.com/docs/index Documentation of key conceptual components within the Pathom framework, including Resolvers, Environment, Planner, and Runner, outlining their roles in processing requests. ```APIDOC Resolver: Description: Building block that establishes relationships between attributes. Pathom traverses a graph of attribute relationships, defined by resolvers, given a user request. Environment: Description: A map containing all necessary context to process a request. Includes Pathom internals and user-defined data (e.g., database connections) available to resolvers. Planner: Description: Given the environment, initial data, and a request, the planner is responsible for figuring out what resolvers to call and in what order. Runner: Description: Executes the plan generated by the planner, traversing and executing resolvers. Handles responses, chooses paths when multiple options are available, and manages errors and conflicts. ``` -------------------------------- ### Pathom Environment Setup and Multi-Page Query Source: https://pathom3.wsscode.com/docs/tutorials/hacker-news-scraper This code block demonstrates how to set up the Pathom environment by defining an `env` map and registering all the necessary resolvers, including the newly created and refactored ones. It then provides a commented example of a Pathom EQL query. This query fetches news titles from the initial page and subsequently from the next linked page, illustrating how to traverse simple pagination using the defined resolvers. ```Clojure ; remember to update env to include all resolvers (def env (-> {::durable-cache* cache*} (pci/register [news-page-html-string news-page-hickory news-page news-next-page]))) (comment ; get titles from first and second page (p.eql/process env [{:hacker-news.page/news [:hacker-news.item/title]} {:hacker-news.page/news-next-page [{:hacker-news.page/news [:hacker-news.item/title]}]}])) ``` -------------------------------- ### Pathom Boundary Interface Full Setup and Usage Source: https://pathom3.wsscode.com/docs/eql This comprehensive Clojure example illustrates the complete process of setting up a Pathom environment with resolvers and then interacting with it via the boundary interface. It showcases various request types, including simple EQL queries, EQL with root entity data, and AST-based queries for optimized performance. ```Clojure (ns com.wsscode.pathom3.docs.demos.core.eql (:require [com.wsscode.pathom3.interface.eql :as p.eql] [com.wsscode.pathom3.connect.indexes :as pci] [com.wsscode.pathom3.connect.built-in.resolvers :as pbir] [com.wsscode.pathom3.connect.operation :as pco]) (:import (java.util Date))) (pco/defresolver area [{:geo/keys [width height]}] {:geo/area (* width height)}) (def env (pci/register [(pbir/constantly-fn-resolver ::now (fn [_] (Date.))) area])) (def pathom (p.eql/boundary-interface env)) ; request EQL (pathom [::now]) ; send root entity data (pathom {:pathom/entity {:geo/width 10 :geo/height 8} :pathom/eql [:geo/area]}) ; use AST, this way Pathom doesn't have to decode the EQL (pathom {:pathom/ast {:type :root :children [{:type :prop :key ::now :dispatch-key ::now}]}}) ``` -------------------------------- ### Querying Attributes with Pathom EQL Interface Source: https://pathom3.wsscode.com/docs/index Demonstrates how to use Pathom's EQL (Extensible Query Language) interface to process a query for an attribute. It shows providing the indexes, initial data, and the requested attribute. ```Clojure (p.eql/process indexes {:city "Recife"} [:cold?]) ; => {:cold? false} ``` -------------------------------- ### Updating Pathom Indexes with New Resolver Source: https://pathom3.wsscode.com/docs/index Shows how to update Pathom's indexes to include a newly defined resolver, such as the `cold?` resolver, alongside existing ones. ```Clojure (def indexes (pci/register [temperature-from-city cold?])) ``` -------------------------------- ### Command-Line Execution Examples for Pathom3 App Source: https://pathom3.wsscode.com/docs/tutorial Provides examples of how to execute the Pathom3 application from the command line using `clj -X`. It demonstrates passing an IP address directly or dynamically fetching it, as well as providing latitude and longitude, highlighting the flexibility of the Pathom3 input arguments. ```Shell # some specific IP clj -X:ip-weather :ip '"198.29.213.3"' # => It's currently 8.33C at {:ip "198.29.213.3"} # get from your IP clj -X:ip-weather :ip "\"$(curl -s ifconfig.me)\"" # => It's currently ??C at {:ip "YOUR_IP"} # from lat long clj -X:ip-weather :latitude '"41.5119"' :longitude '"-88.0569"' ``` -------------------------------- ### Extend Pathom 3 Environment with `env-wrap-plugin` Source: https://pathom3.wsscode.com/docs/built-in-plugins This example shows how to use `pbip/env-wrap-plugin` to inject dynamic data into the Pathom environment at the start of each request. It demonstrates setting a `:now` attribute with the current timestamp and then processing a query to retrieve it. ```Clojure (def env (-> (pci/register (pbir/constantly-fn-resolver :time :now)) (p.plugin/register [(pbip/env-wrap-plugin #(assoc % :now (java.util.Date.)))]))) (p.eql/process env [:time]) ; => {:time #inst "..."} ``` -------------------------------- ### Defining a Resolver for City Temperature Source: https://pathom3.wsscode.com/docs/index Illustrates how to define a Pathom resolver (`pco/defresolver`) that takes a city as input and returns its temperature. It uses a mock 'temperatures' database for demonstration purposes. ```Clojure (def temperatures {"Recife" 23}) (pco/defresolver temperature-from-city [{:keys [city]}] {:temperature (get temperatures city)}) ``` -------------------------------- ### Pathom3 Environment Setup with Alias Resolver and EQL Query Example Source: https://pathom3.wsscode.com/docs/tutorials/hacker-news-scraper This snippet demonstrates how to update the Pathom3 environment to include an alias resolver, which allows mapping `:hacker-news.item/author-name` to `:hacker-news.user/id`. This enables querying user-specific data like karma directly from item authors. The commented section provides an example EQL query to process data from the environment, retrieve news items with author names and user karma, and then sort them by karma. ```Clojure ; update env (def env (-> {::durable-cache* cache*} (pci/register [; alias the author name to id (pbir/alias-resolver :hacker-news.item/author-name :hacker-news.user/id) news-page-html-string news-page-hickory news-page news-next-page user-data-hickory user-data]))) (comment (->> (p.eql/process env [{:hacker-news.page/news [:hacker-news.item/author-name :hacker-news.user/karma]}]) :hacker-news.page/news (sort-by :hacker-news.user/karma #(compare %2 %)))) ``` -------------------------------- ### Pathom 3 Index: Resolver Structure Example Source: https://pathom3.wsscode.com/docs/indexes This example demonstrates the structure of the `index-resolvers` in Pathom 3, which allows looking up resolvers by their name. It shows the configuration for two resolvers: `product-by-id` and `full-name`, detailing their inputs, outputs, and provided attributes. ```Clojure {:com.wsscode.pathom3.connect.indexes/index-resolvers {product-by-id #com.wsscode.pathom3.connect.operation.Resolver {:config {:com.wsscode.pathom3.connect.operation/input [:acme.product/id] :com.wsscode.pathom3.connect.operation/requires {:acme.product/id {}} :com.wsscode.pathom3.connect.operation/output [:acme.product/name :acme.product/price :acme.product/in-stock?] :com.wsscode.pathom3.connect.operation/provides {:acme.product/name {} :acme.product/price {} :acme.product/in-stock? {}} :com.wsscode.pathom3.connect.operation/op-name product-by-id} :resolve product-by-id--23075} full-name #com.wsscode.pathom3.connect.operation.Resolver {:config {:com.wsscode.pathom3.connect.operation/input [::first-name ::last-name] :com.wsscode.pathom3.connect.operation/requires {::first-name {} ::last-name {}} :com.wsscode.pathom3.connect.operation/output [::full-name] :com.wsscode.pathom3.connect.operation/provides {::full-name {}} :com.wsscode.pathom3.connect.operation/op-name full-name} :resolve full-name--23079}}} ``` -------------------------------- ### Querying Attributes with Pathom Smart Map Source: https://pathom3.wsscode.com/docs/index Demonstrates how to use Pathom's Smart Map interface to query for attributes. It shows accessing initial data and dynamically resolving attributes not initially present in the map but reachable via resolvers. ```Clojure (def smart-map (psm/smart-map indexes {:city "Recife"})) (:city smart-map); => "Recife" (:temperature smart-map); => 23 ``` -------------------------------- ### Representing Dependencies with index-oir (PI/TAU Example) Source: https://pathom3.wsscode.com/docs/planner This example shows the `index-oir` representation for the `pi` and `tau` resolvers, simplifying the graph visualization. It illustrates how `tau` depends on `pi` in this format, along with the corresponding EQL query. ```clojure ; index-oir {:pi {{} #{pi}} :tau {{:pi {}} #{tau}}} ; query [:tau] ``` -------------------------------- ### Representing a 'Cold' Query as a Clojure Map Source: https://pathom3.wsscode.com/docs/index Shows how to represent a query for whether a city is 'cold' using a Clojure map, with a placeholder ('?') for the unknown attribute to be resolved. ```Clojure {:city "Recife" :cold? ?} ``` -------------------------------- ### Complete Pathom3 Application for IP-to-Weather Data Source: https://pathom3.wsscode.com/docs/tutorial Presents a full Clojure application that integrates Pathom3 resolvers and EQL processing. The `main` function uses `p.eql/process-one` to fetch temperature based on an input IP, showcasing a complete, runnable example of a Pathom3-powered data fetching service. ```Clojure (ns com.wsscode.pathom3.demos.ip-weather (:require [cheshire.core :as json] [com.wsscode.pathom3.connect.indexes :as pci] [com.wsscode.pathom3.connect.operation :as pco] [com.wsscode.pathom3.interface.eql :as p.eql] [org.httpkit.client :as http])) (pco/defresolver ip->lat-long [{:keys [ip]}] {::pco/output [:latitude :longitude]} (-> (slurp (str "https://get.geojs.io/v1/ip/geo/" ip ".json")) (json/parse-string keyword) (select-keys [:latitude :longitude]))) (pco/defresolver latlong->temperature [{:keys [latitude longitude]}] {:temperature (-> @(http/request {:url (str "http://www.7timer.info/bin/api.pl?lon=" longitude "&lat=" latitude "&product=astro&output=json")}) :body (json/parse-string keyword) :dataseries first :temp2m)}) (def env (pci/register [ip->lat-long latlong->temperature])) (defn main [args] (let [temp (p.eql/process-one env args :temperature)] (println (str "It's currently " temp "C at " (pr-str args))))) ``` -------------------------------- ### Pathom 3 Common Namespace Aliases for Clojure Source: https://pathom3.wsscode.com/docs/index This snippet provides a comprehensive list of namespace aliases used throughout the Pathom 3 documentation. It categorizes aliases into main, common, and less common groups, helping users understand and replicate the examples provided in the documentation. ```Clojure ; 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] ``` -------------------------------- ### Pathom EQL Query Examples for Hacker News Data Source: https://pathom3.wsscode.com/docs/tutorials/hacker-news-scraper This section provides a series of Pathom EQL query examples demonstrating how to interact with the defined Hacker News data environment. It covers setting up the Pathom environment with various resolvers and then executing queries to fetch top-level comments, full structured comments with user karma, and comprehensive news page data including nested comments and user details. ```Clojure (def env (-> {::durable-cache* cache*} (pci/register [(pbir/alias-resolver :hacker-news.item/author-name :hacker-news.user/id) ; add this new one to also alias from comment author name (pbir/alias-resolver :hacker-news.comment/author-name :hacker-news.user/id) news-page-html-string news-page-hickory news-page news-next-page all-news-pages user-data-hickory user-data item-page-hickory item-data item-comments-flat item-comments]))) ``` ```Clojure (comment ; the same we did before, but this time we will only see the top level comments (p.eql/process env {:hacker-news.item/id "25733200"} [{:hacker-news.item/comments [:hacker-news.comment/author-name]}])) ``` ```Clojure (comment ; now, get the whole structured comments, and also include the karma of each user ; on it (p.eql/process env {:hacker-news.item/id "25733200"} [{:hacker-news.item/comments [:hacker-news.comment/author-name :hacker-news.user/karma {:hacker-news.comment/responses '...}]}])) ``` ```Clojure (comment ; WARNING: this will trigger a lot of requests, may take some time. Also, HN may ; throttle or block some accesses from you. (p.eql/process env [{:hacker-news.page/news [:hacker-news.item/age :hacker-news.item/author-name :hacker-news.item/id :hacker-news.item/comments-count :hacker-news.item/score :hacker-news.item/rank-in-page :hacker-news.item/source :hacker-news.item/title :hacker-news.item/url :hacker-news.user/id :hacker-news.user/karma :hacker-news.user/join-date {:hacker-news.item/comments [:hacker-news.comment/author-name :hacker-news.comment/age :hacker-news.comment/content :hacker-news.comment/id :hacker-news.user/id :hacker-news.user/karma :hacker-news.user/join-date {:hacker-news.comment/responses '...}]}]}])) ``` -------------------------------- ### Generate Pathom Execution Plan for a Single Resolver Source: https://pathom3.wsscode.com/docs/planner This Clojure code snippet demonstrates how to define a simple Pathom resolver, register it, and then compute an execution run graph (plan) for a basic EQL query. It shows the necessary setup using `com.wsscode.pathom3.connect.indexes`, `com.wsscode.pathom3.connect.operation`, `com.wsscode.pathom3.connect.planner`, and `edn-query-language.core`. ```Clojure (ns com.wsscode.pathom3.docs.planner (:require [com.wsscode.pathom3.connect.indexes :as pci] [com.wsscode.pathom3.connect.operation :as pco] [com.wsscode.pathom3.connect.planner :as pcp] [edn-query-language.core :as eql])) ; start with a simple resolver (pco/defresolver answer [] {::answer-to-everything 42}) ; create an environment with the indexes (def env (pci/register answer)) ; generate execution DAG (let [ast (eql/query->ast [::answer-to-everything])] (pcp/compute-run-graph (assoc env :edn-query-language.ast/node ast))) ``` -------------------------------- ### Pathom 3 Index: Mutation Structure Example Source: https://pathom3.wsscode.com/docs/indexes This example illustrates the structure of the `index-mutations` in Pathom 3, used for looking up mutations by their name. It includes a `defmutation` definition and the corresponding index entry for `create-product`, showing its provided attributes, parameters, and output. ```Clojure ;; the mutations (pco/defmutation create-product [{:keys [acme.product/name acme.product/price acme.product/in-stock?]}] {::pco/output [:acme.product/id]} (create-product name price in-stock?)) {:com.wsscode.pathom3.connect.indexes/index-mutations {demos.async-mutations/create-product {:config {:com.wsscode.pathom3.connect.operation/provides {:acme.product/id {}}, :com.wsscode.pathom3.connect.operation/params [:acme.product/name :acme.product/price :acme.product/in-stock?], :com.wsscode.pathom3.connect.operation/output [:acme.product/id], :com.wsscode.pathom3.connect.operation/op-name demos.async-mutations/create-product}, :mutate #object[demos.async_mutations$create_product__19314 0x2e4c1d84 "demos.async_mutations$create_product__19314@2e4c1d84"]}}} ``` -------------------------------- ### Registering Resolvers with Pathom Indexes Source: https://pathom3.wsscode.com/docs/index Shows how to register a resolver (e.g., `temperature-from-city`) into Pathom's indexes. These indexes are crucial for Pathom to traverse and understand attribute relationships. ```Clojure (def indexes (pci/register [temperature-from-city])) ``` -------------------------------- ### Example Pathom Query for Flat Comments Source: https://pathom3.wsscode.com/docs/tutorials/hacker-news-scraper A commented example demonstrating how to use Pathom's `p.eql/process` function to query the `item-comments-flat` resolver. This query retrieves the author names of all flat comments associated with a specific Hacker News item ID (e.g., '25733200'). It serves as a test or usage example for the previously defined resolvers. ```Clojure (comment ; get all comments author names (p.eql/process env {:hacker-news.item/id "25733200"} [{:hacker-news.item/comments-flat [:hacker-news.comment/author-name]}])) ``` -------------------------------- ### Create and Use a Basic Pathom Smart Map Source: https://pathom3.wsscode.com/docs/smart-maps This example demonstrates how to define a resolver, register it, create initial data, and then construct a Pathom smart map. It shows how the smart map behaves like a regular map for existing keys and triggers the Pathom engine for derived attributes, caching the result. ```Clojure (ns com.wsscode.pathom.docs.smart-maps-demo (:require [com.wsscode.pathom3.connect.indexes :as pci] [com.wsscode.pathom3.connect.operation :as pco] [com.wsscode.pathom3.interface.smart-map :as psm])) (pco/defresolver full-name [{::keys [first-name last-name]}] {::full-name (str first-name " " last-name)}) (def indexes (pci/register full-name)) (def person-data {::first-name "Anne" ::last-name "Frank"}) (def smart-map (psm/smart-map indexes person-data)) ; if you lookup for a key in the initial data, it works the same way as a regular map (::first-name smart-map) ; => "Anne" ; but when you read something that's not there, it will trigger the Pathom engine to ; fulfill the attribute (::full-name smart-map) ; => "Anne Frank" ``` -------------------------------- ### Fetch User Avatar URL via Pathom3 Smart Map (Clojure) Source: https://pathom3.wsscode.com/docs/resolvers This snippet showcases Pathom3's declarative data fetching using a 'smart map'. It first registers the resolvers into an index. Then, by providing an initial input map and requesting a desired attribute, the smart map automatically determines the necessary resolvers and their execution order to fulfill the request, abstracting away the procedural steps. Multiple examples demonstrate its flexibility with different starting inputs. ```Clojure ; first, we build an index for the relations expressed by the resolvers (def indexes (pci/register [user-from-id user-avatar-slug user-avatar-url])) ; now instead of reference the functions, we let Pathom figure them out using the indexes (->> {:acme.user/id 2} (psm/smart-map indexes) :acme.user/avatar-url) ; => "http://avatar-images-host/for-id/matt-provider-com" ; to highlight the fact that we disregard the function, other ways where we can change ; the initial data and reach the same result: (->> {:acme.user/email "other@provider.com"} (psm/smart-map indexes) :acme.user/avatar-url) ; => "http://avatar-images-host/for-id/other-provider-com" (->> {:acme.user/avatar-slug "some-slogan"} (psm/smart-map indexes) :acme.user/avatar-url) ; => "http://avatar-images-host/for-id/some-slogan" ``` -------------------------------- ### Clojure: Define Pathom3 Environment with Dedicated Plan Cache Source: https://pathom3.wsscode.com/docs/environment This Clojure example demonstrates how to create a Pathom3 environment that includes a dedicated plan cache. It registers indexes and then uses `pcp/with-plan-cache` to associate a cache with the environment. ```Clojure (def indexes (pci/register ...)) (def env-with-cache (pcp/with-plan-cache indexes)) ``` -------------------------------- ### OR Nodes in index-oir (Different Input Paths) Source: https://pathom3.wsscode.com/docs/planner A more complex `index-oir` example demonstrating how Pathom handles graphs with multiple input paths for an attribute (`:a`). It shows dependencies on `:b` or on `:c` and `:d`, illustrating how the planner considers different ways to fulfill a request. ```clojure ; index-oir {:a {{:b {}} #{a1 a2 a3} {:c {} :d {}} #{a9}} :b {{} #{b}} :c {{:d {}} #{c}} :d {{} #{d}}} ; query [:a] ``` -------------------------------- ### Execute Babashka IP Weather Script Source: https://pathom3.wsscode.com/docs/tutorials/babashka This command-line example demonstrates how to run the Babashka application, passing an IP address as an argument. It shows the typical invocation pattern for Babashka scripts with custom parameters. ```Bash bb pathom-demo.bb :ip '"198.29.213.3"' ``` -------------------------------- ### Test Pathom3 Latitude/Longitude to Temperature Resolver Source: https://pathom3.wsscode.com/docs/tutorial This example demonstrates how to test the `latlong->temperature` resolver in a Clojure REPL. It shows the resolver being called with a map containing latitude and longitude, and the resulting map with the temperature attribute. ```Clojure (latlong->temperature {:longitude "-88.0569", :latitude "41.5119"}) ; => {:temperature -1} ``` -------------------------------- ### Creating a Configurable Pathom3 Plugin Source: https://pathom3.wsscode.com/docs/plugins This example illustrates how to create a plugin that is configurable via a function. The `protect-attributes-plugin` function takes a set of attributes to protect and returns a plugin map with a custom wrapper. It also shows how to register this configurable plugin with a Pathom environment. ```Clojure (defn protect-attributes-plugin [protected-attributes] {::p.plugin/id `protect-attributes-plugin :com.wsscode.pathom3.format.eql/wrap-map-select-entry (fn [mst] (fn [env source {:keys [key] :as ast}] (if (and (contains? source key) (contains? protected-attributes key)) ; the output of this extension must be a map entry or nil ; a vector with two elements would also work, but creating a map entry is ; more efficient (coll/make-map-entry key ::protected-value) (mst env source ast))))}) (def env ; create plugin to protect specific attributes and add it (-> (p.plugin/register (protect-attributes-plugin #{:user/password})) (pci/register [(pbir/static-table-resolver :user/id users)]))) ``` -------------------------------- ### Define a Pathom 3 Mutation with `defmutation` Source: https://pathom3.wsscode.com/docs/mutations Demonstrates how to define a basic mutation in Pathom 3 using the `defmutation` macro. This example takes file path and content as parameters and saves the file to disk using `spit`. ```Clojure (pco/defmutation save-file [{::keys [file-path file-content] :as file}] (spit file-path file-content) file) ``` -------------------------------- ### Pathom Resolver for Unit Conversion (Meters to Feet) Source: https://pathom3.wsscode.com/docs/resolvers This example provides a practical application of Pathom resolvers by defining a function `meter<->feet-resolver` that generates two resolvers for converting between meters and feet. It showcases how to use `pbir/single-attr-resolver` and integrate the generated resolvers into a `smart-map` for querying converted values. ```Clojure (defn meter<->feet-resolver [attribute] (let [foot-kw (keyword (namespace attribute) (str (name attribute) "-ft")) meter-kw (keyword (namespace attribute) (str (name attribute) "-m"))] [(pbir/single-attr-resolver meter-kw foot-kw #(* % 3.281)) (pbir/single-attr-resolver foot-kw meter-kw #(/ % 3.281))])) (let [sm (psm/smart-map (pci/register (meter<->feet-resolver :foo)))] [(-> sm (assoc :foo-m 169) :foo-ft) (-> sm (assoc :foo-ft 358) :foo-m)]) ; => [554.489 109.11307528192624] ``` -------------------------------- ### Clojure: Define Pathom3 Environment with Shared Plan Cache Source: https://pathom3.wsscode.com/docs/environment This Clojure example illustrates how to create and reuse a shared plan cache across multiple Pathom3 environments. It uses `defonce` to ensure the cache atom is initialized only once, then passes it to `pcp/with-plan-cache` for different environments. ```Clojure (defonce plan-cache* (atom {})) (def env-with-cache (pcp/with-plan-cache indexes plan-cache*)) (def other-env-with-cache (pcp/with-plan-cache other-indexes plan-cache*)) ``` -------------------------------- ### Defining a Resolver for Cold Check Source: https://pathom3.wsscode.com/docs/index Illustrates how to define a Pathom resolver (`pco/defresolver`) that determines if a location is 'cold' based on its temperature, returning a boolean value. ```Clojure (pco/defresolver cold? [{:keys [temperature]}] {:cold? (< temperature 20)}) ``` -------------------------------- ### Querying Pathom3 Environment with EQL Source: https://pathom3.wsscode.com/docs/tutorial Illustrates how to use Pathom3's Extensible Query Language (EQL) to query the pre-configured environment for `:temperature` given an `:ip`. This demonstrates Pathom's ability to automatically resolve data dependencies without explicitly calling individual resolvers, simplifying the data retrieval process. ```Clojure (p.eql/process env {:ip "198.29.213.3"} [:temperature]) ; 5.24 ``` -------------------------------- ### Define Pathom3 Registry with Static Table Resolver Source: https://pathom3.wsscode.com/docs/built-in-resolvers Demonstrates how to use `pbir/static-table-resolver` to provide static, structured data for entities based on a given identifier. The example shows defining a registry with multiple static tables and querying data using a smart map. ```Clojure (def registry [(pbir/static-table-resolver :song/id {1 {:song/name "Marchinha Psicotica de Dr. Soup"} 2 {:song/name "There's Enough"}}) ; you can provide a name for the resolver, prefer fully qualified symbols (pbir/static-table-resolver `song-analysis :song/id {1 {:song/duration 280 :song/tempo 98} 2 {:song/duration 150 :song/tempo 130}})]) (let [sm (psm/smart-map (pci/register registry) {:song/id 1})] (select-keys sm [:song/id :song/name :song/duration])) ; => #:song{:id 1, :name "Marchinha Psicotica de Dr. Soup", :duration 280} ``` -------------------------------- ### Representing Attributes as a Clojure Map Source: https://pathom3.wsscode.com/docs/index Demonstrates how to represent a query for temperature in a city using a Clojure map, leaving a placeholder ('?') for the unknown attribute to be resolved by Pathom. ```Clojure {:city "Recife" :temperature ?} ``` -------------------------------- ### Clojure Main Entry Point for IP Weather App Source: https://pathom3.wsscode.com/docs/tutorial Defines the `main` function within the `com.wsscode.pathom3.demos.ip-weather` namespace. This function serves as the primary entry point for the command-line application, accepting an `ip` argument and printing a simple message. ```Clojure (ns com.wsscode.pathom3.demos.ip-weather) (defn main [{:keys [ip]}] (println "Request temperature for the IP" ip)) ``` -------------------------------- ### Clojure Deps Configuration for IP Weather App Source: https://pathom3.wsscode.com/docs/tutorial Defines the project's dependencies and source paths in `deps.edn`. Key dependencies include `cheshire` for JSON parsing, `com.wsscode/pathom3` for the Pathom framework, `org.clojure/clojure` for the language, and `http-kit` for making HTTP requests. ```Clojure {:paths ["src"] :deps {cheshire/cheshire {:mvn/version "5.10.0"} com.wsscode/pathom3 {:mvn/version "2022.10.19-alpha"} org.clojure/clojure {:mvn/version "1.11.1"} http-kit/http-kit {:mvn/version "2.5.3"}}} ``` -------------------------------- ### Execute Clojure App from Command Line Source: https://pathom3.wsscode.com/docs/tutorial Illustrates how to run the Clojure application's `main` function directly from the command line using the `clj -X` command, passing the IP address as a quoted string argument. ```Shell clj -X com.wsscode.pathom3.demos.ip-weather/main :ip '"198.29.213.3"' ``` -------------------------------- ### Pathom3 Attribute Not Requested Error Source: https://pathom3.wsscode.com/docs/error-handling Illustrates the `:com.wsscode.pathom3.error/attribute-not-requested` error type in Pathom3. This error occurs when an attribute's error status is checked, but the attribute itself was not part of the initial EQL request or provided in the initial data. The example reuses the previous setup to demonstrate this specific error type. ```Clojure ; using the same previous setup (let [response (p.eql/process env {:movie/id 1} ; note this time we don't ask for the :user/name [:movie/title])] ; but check for its error (p.error/attribute-error response :user/name)) ; => #:com.wsscode.pathom3.error{:cause :com.wsscode.pathom3.error/attribute-not-requested} ``` -------------------------------- ### Setup Durable Plan Cache for Pathom 3 Source: https://pathom3.wsscode.com/docs/cache This Clojure example demonstrates how to configure a durable plan cache for Pathom 3 using `pcp/with-plan-cache`. It registers a resolver and sets up an environment with a shared atom for the plan cache, ensuring it persists across reloads and optimizes subsequent queries. ```Clojure (ns com.wsscode.pathom3.docs.demos.core.cache (:require [com.wsscode.pathom3.connect.indexes :as pci] [com.wsscode.pathom3.connect.operation :as pco] [com.wsscode.pathom3.connect.planner :as pcp] [com.wsscode.pathom3.interface.eql :as p.eql])) (pco/defresolver full-name [{::keys [first-name last-name]}] {::full-name (str first-name " " last-name)}) ; setup with defonce to keep during reloads (defonce plan-cache* (atom {})) (def env (-> (pci/register full-name) (pcp/with-plan-cache plan-cache*))) (defn handle [tx] (p.eql/process env tx)) ``` -------------------------------- ### Sequential Data Flow with Explicit Resolver Calls Source: https://pathom3.wsscode.com/docs/tutorial Demonstrates an initial approach to data fetching where Pathom3 resolvers are explicitly chained in a sequential `->` (thread-first) macro. This illustrates the step-by-step transformation of an IP address into temperature data before leveraging Pathom's automatic dependency resolution. ```Clojure (-> {:ip "198.29.213.3"} ip->lat-long latlong->temperature) ``` -------------------------------- ### Clojure: Initialize Hacker News Scraper and Parse HTML Source: https://pathom3.wsscode.com/docs/tutorials/hacker-news-scraper Sets up the Clojure namespace with required libraries for HTML parsing and selection. It also loads the Hacker News homepage HTML content and demonstrates how to parse it into a Hickory structure, then select the main 'itemlist' element. ```Clojure (ns com.wsscode.pathom3.docs.demos.tutorials.hacker-news-scrapper (:require [hickory.core :as hc] [hickory.select :as hs])) (defonce sample-html (slurp (str "https://news.ycombinator.com/news"))) (comment ; navigate to table element (->> sample-html (hc/parse) (hc/as-hickory) (hs/select (hs/class "itemlist")) first)) ``` -------------------------------- ### Initial Inefficient Data Structure Example Source: https://pathom3.wsscode.com/docs/dynamic-resolvers This Clojure data structure represents user and company information, which, when fetched manually, leads to multiple inefficient round trips to the remote server due to separate queries for users and each associated company. It highlights the problem of excessive round trips in a non-optimized setup. ```Clojure ; {:user/name "Daren Wolff Jr.", ; :user/ip "85822-1129", ; :company/name "Gladys King Inc"} ; {:user/name "Meda Hegmann", ; :user/ip "09986-9393", ; :company/name "Funk-Stamm"} ; {:user/name "Demarco Padberg", ; :user/ip "40703-7676", ; :company/name "Gladys King Inc"} ; {:user/name "Miss Annabell Kessler", ; :user/ip "39359-0412", ; :company/name "Gladys King Inc"} ; {:user/name "Bobbie Grant", ; :user/ip "81444-2468", ; :company/name "Carter, Harber and Jacobi"} ; {:user/name "Carlo Schmitt", ; :user/ip "03074-6343", ; :company/name "Funk-Stamm"} ; {:user/name "Mayra Raynor", ; :user/ip "82239", ; :company/name "Carter, Harber and Jacobi"}]} ``` -------------------------------- ### Chain Pathom3 Resolvers for IP to Temperature Source: https://pathom3.wsscode.com/docs/tutorial This snippet illustrates the composability of Pathom3 resolvers by chaining `ip->lat-long` and `latlong->temperature`. It shows how an initial IP address can be transformed through multiple resolvers to ultimately yield a temperature, demonstrating a complete data flow. ```Clojure (-> {:ip "198.29.213.3"} ip->lat-long latlong->temperature) ; => {:temperature -1} ``` -------------------------------- ### Compute Average Score with Pathom Nested Inputs Source: https://pathom3.wsscode.com/docs/resolvers Illustrates how to use nested inputs in Pathom to compute the average score of top players. The `top-players-avg` resolver explicitly requests `:player/score` from the nested `:game/top-players` input, allowing for data aggregation across related entities. Includes an example of processing the query to get the result. ```Clojure (pco/defresolver top-players-avg "Compute the average score for the top players using nested inputs." [{:keys [game/top-players]}] ; we have to make the nested input explicit {::pco/input [{:game/top-players [:player/score]}]} {:game/top-players-avg-score (let [score-sum (transduce (map :player/score) + 0 top-players)] (double (/ score-sum (count top-players))))}) (p.eql/process (pci/register [top-players player-by-id top-players-avg]) [:game/top-players-avg-score]) ; => #:game{:top-players-avg-score 387.5} ``` -------------------------------- ### Execute Clojure App Using `deps.edn` Alias Source: https://pathom3.wsscode.com/docs/tutorial Demonstrates the simplified command-line execution of the Clojure application by leveraging the `:ip-weather` alias defined in `deps.edn`, making the command more concise. ```Shell clj -X:ip-weather :ip '"198.29.213.3"' ``` -------------------------------- ### Pathom 3 Index Registration Examples Source: https://pathom3.wsscode.com/docs/resolvers This snippet demonstrates various ways to register resolvers, mutations, and existing indexes with `pci/register` to build Pathom's internal indexes. It shows how to register single resolvers, collections of resolvers, and nested groups, forming a comprehensive environment for Pathom processing. ```Clojure ; create indexes for a single resolver (def single-registry (pci/register some-resolver)) ; creating indexes with a few resolvers (def many-registry (pci/register [resolver-a resolver-b])) ; define a group of resolvers in a variable (def some-resolvers [res-a res-b]) ; now use in combination with other mixes: (def more-registry (pci/register [some-resolvers [other-group with-more [deep-nested resolvers]] and-more])) ; now using indexes as part of registry (def with-indexes (pci/register [some-resolvers many-registry])) ; all together now (def env (pci/register [[resolver-a resolver-b] some-resolvers many-registry])) ``` -------------------------------- ### Run Clojure Server Locally Source: https://pathom3.wsscode.com/docs/tutorials/serverless-pathom-gcf This shell command starts the Clojure server locally on port 13337. It uses the `clojure -X:run` command to execute the project's run alias, which typically starts the web server. ```Shell PORT=13337 clojure -X:run ``` -------------------------------- ### Connect Pathom3 Environment to Pathom Viz Source: https://pathom3.wsscode.com/docs/debugging This comprehensive Clojure code snippet outlines the process of setting up a Pathom3 environment and connecting it to the Pathom Viz application. It includes namespace declarations, resolver definitions, conditional parser connection using `p.connector/connect-env` with a unique `parser-id`, and a commented example of query processing. This code is essential for enabling real-time visualization of your Pathom3 graph. ```Clojure (ns com.wsscode.pathom-viz.connector.demos.pathom3 (:require [com.wsscode.pathom.viz.ws-connector.core :as pvc] [com.wsscode.pathom.viz.ws-connector.pathom3 :as p.connector] [com.wsscode.pathom3.connect.built-in.resolvers :as pbir] [com.wsscode.pathom3.connect.indexes :as pci] [com.wsscode.pathom3.interface.eql :as p.eql])) ; you can use goog.defines on ClojureScript or env vars in Clojure ; the important part is to have a flag to decide when to connect the parser (def CONNECT_PARSER? true) (def registry [(pbir/constantly-resolver :pi Math/PI) (pbir/single-attr-resolver :pi :tau #(* 2 %))]) (def env (cond-> (pci/register registry) CONNECT_PARSER? ; give your environment a unique parser-id, this will ensure reconnects work as ; expected (p.connector/connect-env {::pvc/parser-id `env}))) (comment (p.eql/process env [:tau])) ``` -------------------------------- ### Define Pathom Resolver for Mock Todo List Source: https://pathom3.wsscode.com/docs/resolvers Sets up a basic Pathom resolver for a mock todo list. The `todos-resolver` exposes a list of todos with message and done status. It demonstrates the use of explicit `::pco/output` for better schema definition and tooling support, and shows how to process a simple query. ```Clojure (def mock-todos-db [{::todo-message "Write demo on params" ::todo-done? true} {::todo-message "Pathom in Rust" ::todo-done? false}]) (pco/defresolver todos-resolver [env _] ; express nested shape {::pco/output [{::todos [::todo-message ::todo-done?]}]} {::todos mock-todos-db}) (def env (pci/register todos-resolver)) ; list all todos (p.eql/process env [::todos]) ``` -------------------------------- ### Pathom3 Lenient Error Handling Example Source: https://pathom3.wsscode.com/docs/error-handling Demonstrates Pathom3's lenient error handling mode where attribute-level errors are reported without failing the entire request. This example defines resolvers for user identity and movie details, registers them with lenient mode enabled, and processes an EQL query to observe partial success and attribute-specific errors when a required input is missing. ```Clojure (ns com.wsscode.pathom3.docs.demos.core.error-handling (:require [com.wsscode.pathom3.connect.operation :as pco] [com.wsscode.pathom3.interface.eql :as p.eql] [com.wsscode.pathom3.connect.indexes :as pci] [com.wsscode.pathom3.error :as p.error])) (def identity-db {1 {:user/name "Martin"}}) (pco/defresolver user-identity [{:keys [user/id]}] {::pco/output [:user/name]} (get identity-db id)) (def movies-db {1 {:movie/title "Bacurau"}}) (pco/defresolver movie-details [{:keys [movie/id]}] {::pco/output [:movie/title]} (get movies-db id)) (def env (pci/register {::p.error/lenient-mode? true} [user-identity movie-details])) (p.eql/process env {:movie/id 1} [:user/name :movie/title]) ; => {:movie/title "Bacurau", ; :com.wsscode.pathom3.connect.runner/attribute-errors {:user/name {:com.wsscode.pathom3.error/cause :com.wsscode.pathom3.error/attribute-unreachable}}} ``` -------------------------------- ### Incorrect Async Resolver Return Pattern Example Source: https://pathom3.wsscode.com/docs/async This example illustrates a common mistake when writing asynchronous Pathom resolvers. Returning a future as the value for a specific key within the output map (e.g., `::age: (p/let ... age)`) is incorrect. Pathom expects the entire resolver's return value to be a future that resolves to the complete output map, not individual key values. ```Clojure (pco/defresolver age-from-name [{::keys [first-name]}] {::pco/output [::age]} ; return map {::age ; with key value as a promise, won't work (p/let [{:keys [age]} (json-get (str "https://api.agify.io/?name=" first-name))] age)}) ``` -------------------------------- ### Execute Pathom 3 Mutation using EQL Source: https://pathom3.wsscode.com/docs/mutations Illustrates how to register a mutation with the Pathom environment and then execute it using the EQL `p.eql/process` function. It demonstrates passing parameters for the mutation and shows the resulting output. ```Clojure (def env (pci/register save-file)) (p.eql/process env [`(save-file {::file-path "./file.txt" ::file-content "contents here"})]) ; => {com.wsscode.pathom3.docs.mutations/save-file ; {:com.wsscode.pathom3.docs.mutations/file-path "./file.txt", ; :com.wsscode.pathom3.docs.mutations/file-content "contents here"}} ``` -------------------------------- ### Pathom `pco/resolver` Helper Syntax Source: https://pathom3.wsscode.com/docs/resolvers This snippet demonstrates two common ways to define a resolver using the `pco/resolver` helper: a concise helper syntax and a more explicit configuration map approach. Both methods achieve the same outcome of defining an operation with specified output and resolution logic. ```Clojure ; using helper syntax (pco/resolver `op-name {::pco/output [::foo]} (fn [env input] {::foo "bar"})) ; using config map (pco/resolver {::pco/op-name `op-name ::pco/output [::foo] ::pco/resolve (fn [env input] {::foo "bar"})}) ``` -------------------------------- ### Example Data for Comment Tree Transformation Source: https://pathom3.wsscode.com/docs/tutorials/hacker-news-scraper This snippet provides a conceptual example of the data transformation required to convert a flat list of comments (with IDs and indentation levels) into a hierarchical tree structure. It visually represents the desired nesting and then shows the input and target output data structures using Clojure map syntax, illustrating how comments with higher indentation become children of preceding comments. ```Clojure ; first, visually, it looks like this: ; 1 ; | 2 ; | 3 ; 4 ; | 5 ; | | 6 ; we have a list like this, with ids and indentations, the rest of the data we can ignore ; for the purpose of this transformation [{:id 1 :ident 0} {:id 2 :ident 1} {:id 3 :ident 1} {:id 4 :ident 0} {:id 5 :ident 1} {:id 6 :ident 2}] ; and our goal is to transform that into: [{:id 1 :children [{:id 2} {:id 3}]} {:id 4 :children [{:id 5 :children [{:id 6}]}]}] ```