Hooks give developers a way to add custom logic to the framework of lists, fields and operations Keystone provides.
This document provides an overview of the concepts, patterns and function of the Keystone hook system. The Hooks API docs describe the specific arguments and usage information.
There are several categorisations that can be applied to hooks and are useful for understanding what is run and when.
Note: the concepts listed here have some exceptions. See the Gotchas section.
Keystone defines several stages within the hook execution order. These stages are intended to be used for different purposes; they help organise your hook functionality.
- Input resolution - modify the values supplied
- Data validation - check the values are valid
- Before operation - perform side effects before the primary operation
- After operation - perform side effects after the primary operation
Hooks are available for these core operations:
These operations are reused used for both "single" and "many" modes.
deleteUser (singluar) and
deleteUsers (plural) mutations are both considered to be
Hooks for these operations have different signatures due to the nature of the operations being performed. See the Hook API docs for specifics.
Note: Keystone does not currently implement
A hooks type is defined by where it is attached. Keystone recognises three types of hook:
- Field Type hooks - Field Type hooks are associated with a particular field type and are applied to all fields of that type across all lists.
- Field hooks -
Field hooks can be defined by the app developer by specifying the
hooksattribute of a field configuration when calling
- List hooks -
List hooks can be defined by the app developer by specifying the
hooksattribute of a list configuration when calling
For most stage and operation combinations, different functions (hooks) can be supplied for each hook type. This group of distinct but related hooks are referred to as a hook set.
beforeDelete function could be supplied for a list, several specific fields on the list and a field type used by the list.
All hooks in a hook set share the same functional signature but are invoked at different times.
See the Hooks API docs and Intra-Hook Execution Order section for more information.
In total there 7 hook sets available. This table shows the hook set relevant to each combination of stage and operation:
delete hook sets can be attached as list, field or field type hooks.
Due to their similarity, the
update operations share a single set of hooks.
To implement different logic for these operations make it conditional on either the
for create operations
existingItem will be
See the Hooks API docs for argument details and usage.
The hooks are invoked in a specific order during an operation. For full details of the mutation lifecycle, and where hooks fit within this, see the Mutation Lifecycle Guide.
- Access control checks
- Field defaults applied
resolveInputcalled on all fields, even if they are not defined in the supplied data
validateInputcalled on all fields which have a resolved value (after all
resolveInputcalls have returned)
beforeChangecalled on all fields which have a resolved value (after all
validateInputcalls have returned)
- Database operation
afterChangecalled on all fields, even if their value was not changed
- Access control checks
validateDeletecalled on all fields
beforeDeletecalled on all fields (after all
validateDeletecalls have returned)
- Database operation (after all
beforeDeletecalls have returned)
afterDeletecalled on all fields (after the DB operation has completed)
Within each hook set, the different hook types are invoked in a specific order.
- All relevant and defined field type hooks are invoked in parallel
- All relevant and defined field hooks are invoked in parallel
- If defined the list hook is invoked
The hook system is powerful but it's breadth and flexibility introduce some complexity. A few of the main stumbling blocks are:
updateoperations share a single set of hooks. To implement different logic for these operations make it conditional on either the
existingItemarguments; for create operations
- As per the table above, the
deleteoperations have no hook set for the input resolution stage. This operation doesn't accept any input (other than the target IDs).
- Keystone does not currently implement
- Field type hooks and field hooks run in parallel.
These nuances aren't bugs per se -- they generally exist for good reason -- but they can make understanding the hook system difficult.