Plugins provide a way to extend Pathom behavior. This page explains how Pathom plugins work and how you can write your extensions.
The wrapping model of plugins
Pathom extensions work by
wrapping code points in Pathom. This means you can code
around the extended points in Pathom.
Each extension point has an identifier keyword.
Let's take for example the
extension. This adds a hook to the result masking operation, which occurs at the
p.eql/process to filter out the results according to the query.
It's a Pathom convention to prefix all plugin wrapping attributes with
This hook, in specific, can control how the value is placed at the resulting map.
Let's use this hook to make an extension that protects certain attributes from getting in the output map:
The data from those attributes may still leak in the meta for stats, to make it really secure you need to prevent regular users from seeing the stats.
Now let's talk in more detail about the things we used in this example.
Define the wrapper fn
A more generic way to think about wrapper extensions is with the following structure:
Define the plugin
Then, a plugin consists of one or more of those wrapping functions. Any map can be a
plugin, to make it a plugin, you must add the key
::p.plugin/id with a symbol to
identify this plugin. Then each other key is an extension wrapper.
The latter form is interesting when you are generating a plugin programmatically.
For example, we could make our plugin to protect attributes more configurable by using making a function that returns the plugin:
To use a plugin, you must add it to the environment using the
This function works similar to
pci/register and also accepts vectors
In the case of plugins, there is also
p.plugin/register-after. Which gives you more control over the plugin execution order.
In the next section, we will talk more about plugin order.
To understand the order in which plugins execute, it helps to keep the following image in your head:
Here is the same description as code:
Plugins on top will enter first and leave last.
You can view the plugin order in the attribute
::p.plugin/plugin-order attribute in
A plugin can halt the execution of the posterior plugins (and the original operation itself). This happens when the plugin returns some data without calling the original function given on the wrapper.
For example, if
plugin-b halts execution (like our protected attributes plugin does)
the stack stops there and goes back up. The following image illustrates this case:
Consider this when deciding the order of plugins.
When you use
p.plugin/register, Pathom creates specific lists for each plugin type. This way, it only has to iterate over plugins of that extension type.
Here you can see all the extension points available in Pathom.
Extensions available for the runner process of Pathom at
Wrap the call of a resolver. Note in the case of a batch resolver, input is going to be a sequence.
Wrap the call of a mutation.
Wrap the error data, allowing you to modify it.
Wrap the operation that attaches an error to a node.
Wrap batch errors
Use this to get notified when mutation errors happen.
Wrap the operation of merging new data in the entity. This is an important point. You
can control how sub-processes occur here. The source original is an
assoc like operation.
This runs only once per entity, if you want to do any kind of post-process that you expect one entity to be ready.
Wrap the operation of running a graph. Note for a given transaction, there may be many graphs to run. Each entity (each map in the result) is potentially an entity and may have its own graph.
Also note this may run multiple times for the same entity, for each "batch hit" it does.
Like the previous, but only runs at the root graph.
This is the one we used for the initial demo. Controls how outputs get masked out in the post-processing of the EQL output.
This wraps a call to
process-ast in the EQL interface.
When the query is triggered using
process, you also get the query as
in the environment.
If you don't see this value it means
process-ast was called directly. To get the query
in this case use
eql/ast->query to generate the query from the AST.