Introduction

The pathom library provides a set of functionality for building robust parsers to process EQL requests.

If you are not familiar with EQL, check its docs for a general intro on EDN Graph Queries.

In case you are not sure what this library is for, check the rationale to understand the motivations for it.

Pathom 3

Hello, if you are new here, I suggest you have a look at Pathom 3!

Pathom 3 still in alpha, but it’s the library that will get updated ahead, and most of the API is already stable, Pathom 2 (current here) is only updating in case of bug fixes.

What does it look like?

Let’s start with a demo! Use the buttons to load pre-defined queries and feel free to edit and try around (start typing inside the [] and it will auto-complete to help you explore the API!).

[:answer-to-everything]

Source code for this demo:

(ns com.wsscode.pathom.book.intro.demo
  (:require [com.wsscode.pathom.core :as p]
            [com.wsscode.pathom.connect :as pc]))

(pc/defresolver answer [_ _]
  {::pc/output [:answer-to-everything]}
  {:answer-to-everything 42})

(pc/defresolver answer-plus-one [_ {:keys [answer-to-everything]}]
  {::pc/input  #{:answer-to-everything}
   ::pc/output [:answer-plus-one]}
  {:answer-plus-one (inc answer-to-everything)})

(def registry
  [answer answer-plus-one])

(def parser
  (p/parser
    {::p/env     {::p/reader               [p/map-reader
                                            pc/reader2
                                            pc/open-ident-reader
                                            p/env-placeholder-reader]
                  ::p/placeholder-prefixes #{">"}}
     ::p/mutate  pc/mutate
     ::p/plugins [(pc/connect-plugin {::pc/register registry})
                  p/error-handler-plugin
                  p/trace-plugin]}))

(comment
  ; to call the parser and get some data out of it, run:
  (parser {} [:answer-to-everything]))

I hope you had a good time playing around! The intention of this library is to make it as easy as possible to implement API’s like the one you saw you in the demo above.

How it works

The following diagram illustrates the main pieces that make a pathom parser:

parser diagram

To break down in parts, let’s imagine we are processing the following request:

[:answer-to-everything :answer-plus-one]

Parser

The parser receives the whole query to process and iterates over each attribute calling the reader on each one of them.

In our example, the parser receives the full query [:answer-to-everything :answer-plus-one].

Readers

A reader processes a single entry from the query, the entry can be a property, a join or an ident. It is common to have a chain of readers (as shown in the photo and demo), when it’s a chain, it tries each reader in order. A reader can respond with either a value or signal the engine that it can’t process the key, in which case, it triggers the next reader.

In our example, the reader gets called once for :answer-to-everything and then again with :answer-plus-one.

Resolvers

Pathom is capable of traversing a graph of dependencies to resolve data. This feature is called Connect and each resolver is an edge of this graph traversal. You can find more information about them in connect resolvers docs.

Mutation

When you run EQL mutations, the mutate function will be called and the primary way to handle them in pathom is by defining Connect mutations.

In the Pathom library

The library includes:

  • A composable reader abstraction.

  • The concept of entity, which works as a base framework for reusable and sharable readers.

  • A plugin system with some built-in plugins:

    • Error handler: Handles errors at an attribute level.

    • Request cache: For caching the results of possibly repetitive parsing which can happen on a single request.

    • Tracer: A plugin to measure and debug each step of the parsing process.

  • Connect: A higher-level abstraction that can resolve attribute relationships automatically. For example, automatic traversal of database joins or resolving data through network requests. This enables exploratory capabilities and a much simpler access model when the need arises to do extra work for resolving a single conceptual join.

  • GraphQL integration: Use GraphQL endpoints directly from your query system (in development).

Most people find the most leverage in the Connect features, which allows you to quickly build dynamic query processing systems to satisfy client data requests with ease.

Aliases Used in Code Examples

Throughout the book, our code examples use aliases instead of explicit namespaces. The aliases used, assume that you have the following namespace requires in your environment:

(ns my-namespace
  (:require
    [com.wsscode.pathom.core :as p]
    [com.wsscode.pathom.connect :as pc]
    [com.wsscode.pathom.connect.graphql2 :as pcg]
    [com.wsscode.pathom.graphql :as pg]
    [com.wsscode.pathom.sugar :as ps]
    [com.wsscode.pathom.trace :as pt]
    ; clj only
    [com.wsscode.common.async-clj :refer [let-chan go-catch <? <?maybe]
    ; cljs only
    [com.wsscode.common.async-cljs :refer [let-chan <!p go-catch <? <?maybe]]))

So, any time you see the usage of a namespace in a keyword or a function like p/parser or ::p/reader you should remember that these are the namespaces involved.

Presentations

Contributing

In every page of this documentation, you can find an icon at the top right, click on that icon to edit the current page (it opens the Github link to edit the page source).

How to Use This Library

Most of our current user base is made up of Fulcro users, however this library is a stand-alone thing that you can use to fulfill any system using EQL queries. The purpose of this library is to make it much easier to build code that can process EQL on both the client and server-side. We expect you to have one or more of the following needs:

  • You want to fulfill a client UI query from some server-side data source(s).

  • You want to build a client-side parser for directly filling local UI data queries from a local data source.

  • You want to build a parser (client or server) that uses async APIs to fulfill different parts of a query. Perhaps gluing together data sources from various micro-services.

  • You want to use a GraphQL API from the client.

  • You want to provide third-party users a GraphQL API (Future Work)

When building most parsers, you’ll want to use Pathom Connect.

To process EQL queries against GraphQL you’ll use the GraphQL Integration.