Pathom 3 is strict by default. It's an all-or-nothing mode, or Pathom will fulfill the
full query, or it will throw an exception.
Here are the reasons that may cause Pathom to throw:
Attribute doesn't exist in the indexes
Attribute exists in the indexes, but the available data isn't enough to reach it
Attribute wasn't in the final output after all the processes
A resolver threw an exception*
note
An exception to this rule is if a resolver is contained in a OR node, if there are
multiple options to fetch some attribute. It only throws when every option fails.
Let's see one example of each of those cases:
; Attribute doesn't exist in the indexes
(p.eql/process
(pci/register
(pbir/constantly-resolver :a "foo"))
[:b])
; => EXCEPTION: Pathom can't find a path for the following elements in the query: [:b] at path []
; Attribute exists in the indexes, but the available data isn't enough to reach it
(p.eql/process
(pci/register
(pbir/single-attr-resolver :a :b "foo"))
[:b])
; => EXCEPTION: Pathom can't find a path for the following elements in the query: [:b] at path []
; An extended version of this is also true in case of nested inputs, if Pathom can't see
; a path for a nested input requirement, it will consider it a plan failure.
(p.eql/process
(pci/register
[(pco/resolver 'nested-provider
{::pco/output [{:a [:b]}]}
(fn [_ _]
{:a {:b 1}}))
(pco/resolver 'nested-requires
{::pco/input [{:a [:c]}]
::pco/output [:d]}
(fn [_ _]
{:d 10}))])
[:d])
; => EXCEPTION: Pathom can't find a path for the following elements in the query: [:c] at path [:a]
; Attribute wasn't in the final output after all the processes
(p.eql/process
(pci/register
(pco/resolver 'data
{::pco/output [:a :b :c]}
(fn [_ _]
{:a 10})))
[:c])
; => EXCEPTION: Required attributes missing: [:c] at path []
; A resolver threw an exception
(p.eql/process
(pci/register
(pco/resolver 'error
{::pco/output [:error]}
(fn [_ _]
(throw (ex-info "Deu ruim." {})))))
[:error])
; => EXCEPTION: Deu ruim.
This mode is good when:
You run Pathom and use the results in-process (vs over the wire)
When your Pathom scenario involves large queries (for a complex UI made with Fulcro
for example) you probably don't want that a single attribute break the whole query.
This requires a more tolerant mode, which is the lenient mode in Pathom 3.
In lenient mode, errors are handled per attribute. The following sections will explain
how to handle errors in this way.
Pathom is an abstraction around attributes, and the error handling follow this idea by
thinking of errors as things that happen per attribute.
info
This is counter-intuitive from new people using Pathom, especially for users used to deal
with REST, where is normal to expect a request to either succeed or fail.
Here is an example to make the concept more concrete:
Considering the request and response, did this request succeed or failed?
Hard to say at the "request level", but at the attribute level it's clear: :movie/title
succeed and :user/name failed. At your application-level you must take this partial
failure property into account to decide what to do.
Note in lenient mode, Pathom adds the attribute ::pcr/attribute-errors when there
is some attribute error. This map contains the error details for each attribute.
The error indicates the attribute :user/name wasn't reachable, which makes sense given we
didn't provide the :user/id required for it (and neither there is a resolver to
provide it).
At this point, it means Pathom expected to fulfill the attribute, but something failed
in the process.
The Pathom Plan includes an index that says which node is expected to
return the value for each attribute (and if there are multiple options for an attribute,
it will have multiple nodes associated with).
For each of the nodes, Pathom will look for errors (check again on the diagram to see
the flow).
When the resolver throws an exception, the runner stores this associated with the node
that invoked the resolver. This is the first thing that Pathom looks for, and if there
is an error there, it returns the error.