Mutations
Mutations are a mechanism to run procedures through EQL. For a syntax review please check the EQL Mutations specification.
Using defmutation
To create mutations you can use the defmutation
helper, which is similar to the defresolver
macro. And similarly to resolvers,
it can take a variable number of arguments - none, one ([parameters]
), or two ([env parameters]
).
Here is a mutation to save a file on disk:
(pco/defmutation save-file [{::keys [file-path file-content] :as file}]
(spit file-path file-content)
file)
Just like defresolver
, one difference here is that in mutations there is no input
,
mutations always use parameters
.
This is a subtle difference, but you can see it by inspecting the mutation:
save-file
=>
; #com.wsscode.pathom3.connect.operation.Mutation
; {:config #:com.wsscode.pathom3.connect.operation
; {:params [:com.wsscode.pathom3.docs.mutations/file-path
; :com.wsscode.pathom3.docs.mutations/file-content],
; :op-name com.wsscode.pathom3.docs.mutations/save-file},
; :mutate #object[...]}
Note what would be ::pco/input
on a defresolver
, it's now ::pco/params
in mutation.
Different from inputs
, parameters don't have auto-resolution, they always come as-is,
but having this information can help on creating extensions (to support auto-resolution
if wanted) and help to understand the system (documentation).
To run the mutation you need to use the EQL interface:
(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"}}
The result of the mutation result comes in the same key as the mutation name.
By default the mutation symbol is the fully qualified var name of the mutation. Note we use the backtick to use the complete name.
Mutations are the first thing the runner executes, this way you know the reads from the query will have update values, in case the mutation affects something related to them.
Mutation joins
You can also make a join in the mutation to specify what you want from the result. For this example we will write a resolver to get the file size, and use it as part of the mutation request:
(pco/defmutation save-file [{::keys [file-path file-content] :as file}]
(spit file-path file-content)
file)
(pco/defresolver file-size [{::keys [file-path]}]
{::file-size (.length (io/file file-path))})
(def env (pci/register [save-file file-size]))
(p.eql/process env
[{`(save-file {::file-path "./file.txt" ::file-content "contents here"})
[::file-path
::file-size]}])
; => {com.wsscode.pathom3.docs.mutations/save-file
; {:com.wsscode.pathom3.docs.mutations/file-path "./file.txt",
; :com.wsscode.pathom3.docs.mutations/file-size 13}}
Changing mutation symbol
I recommend sticking to fully qualified names, but in case you need a different name
you can use the ::pco/op-name
to make it something else:
(pco/defmutation save-file [{::keys [file-path file-content] :as file}]
{::pco/op-name 'io/save-my-file}
(spit file-path file-content)
file)
; then you can use this name to call the mutation
(p.eql/process env
['(io/save-my-file {::file-path "./file.txt" ::file-content "contents here"})])
Resolve mutation params
On Pathom, when you call a mutation, the params flow without any alteration. The default behavior is meant to give the fastest path possible to invoke mutations.
That said, it's common also to want the params to be "resolved", just like Pathom does with inputs on resolvers.
To make that happen, you can use the mutation-resolve-params
built-in plugin. When
you add this plugin, Pathom will use the ::pco/params
as a query and will run that
on the user params, and then deliver the resolved data to the mutation.
Example:
(pco/defmutation foo [{:keys [b]}]
{:res b})
(p.eql/process
(-> (pci/register
[(pbir/single-attr-resolver :a :b inc)
foo])
(p.plugin/register pbip/mutation-resolve-params))
['(foo {:a 1})])
; => {foo {:res 2}}
When using pco/defmutation
, ::pco/params
is inferred from the argument destructuring.