The goal of using EQL is to express some data shape (hierarchy) without the values and
let Pathom fill in the values.
Using EQL is also the most efficient way to request multiple things at once with Pathom.
With EQL, Pathom knows the full request ahead of time. Therefore, Pathom can use this information
to optimize the planning and execution.
Keep in mind that EQL is about expressing some data hierarchy, to start simple we will
use a flat structure to demonstrate the basic usage of the EQL interface:
Using EQL joins you
can make specific requirements about nested data. In this example we will simulate the
existence of many worlds where PI have different values:
(def indexes
(pci/register
[(pbir/constantly-resolver ::pi 3.1415)
(pbir/single-attr-resolver ::pi ::tau #(* % 2))
; define a resolver to provide a collection of items
(pbir/constantly-resolver ::pi-worlds
[{::pi 3.14}
{::pi 3.14159}
{::pi 6.8}
{::tau 20}
{::pi 10 ::tau 50}])]))
(p.eql/process indexes
; using a map we are able to specify nested requirements from some attribute
Pathom uses the EQL ident as a form to specify a single attribute to start requesting
data from. Here is an example using the resolvers we created before:
(p.eql/process indexes [{[::pi 2.3] [::tau]}])
; => {[::pi 2.3] {::tau 4.6}}
In this example, given PI is 2.3, Tau becomes 4.6, since it's defined as the
double of PI.
You can use placeholders to provide in-query data for Pathom processing. To do this, lets
get back to our famous full name example, the way to provide data is to send it to
a placeholder key as EQL parameters:
When moving to a placeholder context, Pathom inherits the same parent data and merges
the params data to it, to illustrate let's make a nested example of it:
Union queries provide a way to achieve polymorphism in EQL, for a review on the
union syntax refer to the EQL Union specification page.
Consider you want to request information for some user feed. In our feed example, there
are three types of entries: posts, ads and videos. Each type requires different attributes
to render. This is how we can write some resolvers to fetch each type:
To decide which path to take, Pathom looks if the entry data contains the key mentioned
in the union entry key. When they match Pathom picks that path option.
There is a secondary option, this is more intended for implementors of dynamic resolvers.
If the meta contains the meta-data ::pf.eql/union-entry-key, Pathom will use that
as the union entry key, example:
In EQL queries, you can use the special symbol * to ask Pathom to give all the data
available for that entity. In other words, this removes the output filtering at that
level. Here is an example of what it means:
; define a resolver that returns multiple things
(pco/defresolver user-data []
{:user/name "foo"
:user/email "some-user@email.com"
:user/birth-year 1988})
; standard query
(p.eql/process (pci/register user-data)
[:user/name])
; gets the output filtered, only the items in query show up
=> #:user{:name "foo"}
; making query adding the *
(p.eql/process (pci/register user-data)
[:user/name '*])
; now all the data that was loaded in process will show up in the result
Strict errors as data is feature from Pathom 3 2022.02.21-alpha+ versions.
When using the boundary interface, errors that would normally throw when using the EQL
interface directly will be turned into a map format. This is more suitable to a boundary
interface because then we can send this error reporting over the wire with ease.
The error format is opinionated, and it looks like this:
{:com.wsscode.pathom3.error/error-message "Pathom can't find a path for the following elements in the query: [:wrong] at path []",
The boundary API design is to make it flexible, and although is more efficient
to initialize as much as possible before in the env, it still allows extension of
it.
To extend the env you can send another argument to the boundary interface.
For example, let's say we have a resolver to get data from the current user. The current
user is something we like to set at the start of the process, but we don't want to allow
users to override it (for security reasons).
In this case we can set the user id on env when we call the boundary interface: