Cloud-safe hooks
Use declarative Jexl-style string hooks that survive schema sync to Dyrected Cloud, know which hook families are supported, and see what sync preserves or strips.
Use Cloud-safe hooks when you want hook behavior that survives sync:schema and runs from the schema Dyrected Cloud stores.
The important distinction is not only which hook phase you want, but which hook shape Cloud can actually keep. In practice, that means:
- use declarative Jexl-style string hooks for simple read shaping and value transforms
- use function hooks for self-hosted async work, side effects, and anything that needs arbitrary server code
If you are new to hooks themselves, start with the Hooks overview. This page is the Cloud-specific guide.
The recommended path
For most Cloud projects, the safest path is:
- use declarative Jexl-style string hooks for
beforeRead,afterRead, andbeforeChange - use declarative Jexl-style
admin.hooks.onChangefor browser-side form reactivity that should survive schema sync - keep
afterChange, delete hooks, and other side-effect hooks as self-hosted function hooks
That avoids the two most common Cloud mistakes:
- a function hook is stripped during
sync:schema - a hook family is technically valid in self-hosted Dyrected but not part of the Cloud-safe subset
What works in Cloud
Dyrected Cloud keeps the following declarative hook surfaces:
| Hook surface | Function form | Declarative string form | Survives sync:schema |
|---|---|---|---|
collection/global beforeRead | Yes | Yes | Yes |
collection/global afterRead | Yes | Yes | Yes |
collection/global beforeChange | Yes | Yes | Yes |
collection/global afterChange | Yes | No | No |
collection beforeDelete / afterDelete | Yes | No | No |
field beforeChange | Yes | Yes | Yes |
field afterRead | Yes | No | No |
field admin.hooks.onChange | Yes | Yes | Yes |
field admin.hooks.options | Yes | No | No |
If a hook family is not in the declarative subset, Cloud sync strips it.
What a declarative hook can see
Cloud-safe hooks use the same expression model, but each surface gets a small, explicit context.
Collection and global beforeRead
Use this when you want to reshape the query before the read runs.
Available values:
requserquery
Example:
hooks: {
beforeRead: [
"user != null ? query : { status: { equals: 'published' } }",
],
}Collection and global afterRead
Use this when the stored document is correct, but the returned document should be shaped differently.
Available values:
requserdoc
Example:
hooks: {
afterRead: [
"user != null ? doc : { internalNotes: null }",
],
}When the expression returns an object, Dyrected merges that object into the current document before continuing to the next hook.
Collection and global beforeChange
Use this when you want to patch the incoming write payload before create or update.
Available values:
requserdatadocoperation
Example:
hooks: {
beforeChange: [
"{ slug: data.title }",
],
}When the expression returns an object, Dyrected merges that object into the current data payload before passing it to the next hook.
Field beforeChange
Use this when one field value should be transformed the same way on every write path, including nested object, array, and blocks fields.
Available values:
valuedataoriginalDocuser
Example:
{
name: 'statusLabel',
type: 'text',
hooks: {
beforeChange: [
"value != null ? value + '!' : value",
],
},
}Field admin.hooks.onChange
Use this when a form field should react to sibling values in the admin UI and you want that declarative behavior to survive schema sync.
Available values:
valuesiblingDatadata
Example:
{
name: 'slug',
type: 'text',
admin: {
hooks: {
onChange: "siblingData.title != null ? siblingData.title : value",
},
},
}This declarative form returns the next field value. It does not support imperative setValue.
What sync:schema preserves and strips
When you run npx dyrected sync:schema, Dyrected intentionally sanitizes hook config for Cloud.
It keeps:
- supported declarative string hooks on collection/global
beforeRead,afterRead, andbeforeChange - supported declarative string hooks on field
beforeChange - declarative string
field.admin.hooks.onChange
It strips:
- function hooks on every hook surface
- unsupported hook families such as
afterChange, delete hooks, fieldafterRead, andadmin.hooks.options
If a mixed hook array contains both strings and functions, Cloud keeps the surviving string hooks in the same order and strips the function entries. If nothing survives, the hook key is omitted.
The CLI warns with the exact schema path for every stripped hook entry.
Dyrected also validates declarative hook expressions early. If a string hook uses unsupported context or invalid syntax, Dyrected points to the exact config path so you can fix it before sync or runtime.
Why the unsupported hooks are still out of scope
The unsupported hook families are the ones most likely to need capabilities that do not belong in synced schema data:
- async work
- external API calls
- DB writes after persistence
- secret-backed integrations
- imperative admin behavior such as recomputing option lists
That is why afterChange, delete hooks, field.afterRead, and admin.hooks.options remain function-only for now. They are better candidates for a later plugin or extension model than for serialized schema execution.
Practical rule of thumb
- If the logic is a synchronous transform of
query,doc,data, orvalue, the declarative string form is probably the right Cloud-safe choice. - If the logic needs side effects, async calls, imperative control flow, or writable server capabilities, keep it as a function hook and treat it as self-hosted-only for now.
Related pages
- Hooks overview for the overall hook model.
- Collection hooks for collection lifecycle guidance.
- Global hooks for singleton lifecycle guidance.
- Field hooks for field-level and admin hook behavior.
- CLI for
sync:schemabehavior.
Overview
Use hooks safely in Cloud as content rules, or run unrestricted function hooks in self-hosted Dyrected.
Declarative Expressions & Helpers
Learn how to write Cloud-safe Jexl expressions for field hooks, reactive form logic, and access control rules using Dyrected's built-in helper utility suite.