Dyrected
Model ContentContent Rules

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.

For most Cloud projects, the safest path is:

  1. use declarative Jexl-style string hooks for beforeRead, afterRead, and beforeChange
  2. use declarative Jexl-style admin.hooks.onChange for browser-side form reactivity that should survive schema sync
  3. 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 surfaceFunction formDeclarative string formSurvives sync:schema
collection/global beforeReadYesYesYes
collection/global afterReadYesYesYes
collection/global beforeChangeYesYesYes
collection/global afterChangeYesNoNo
collection beforeDelete / afterDeleteYesNoNo
field beforeChangeYesYesYes
field afterReadYesNoNo
field admin.hooks.onChangeYesYesYes
field admin.hooks.optionsYesNoNo

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:

  • req
  • user
  • query

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:

  • req
  • user
  • doc

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:

  • req
  • user
  • data
  • doc
  • operation

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:

  • value
  • data
  • originalDoc
  • user

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:

  • value
  • siblingData
  • data

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, and beforeChange
  • 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, field afterRead, and admin.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, or value, 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.

On this page

Dyrected| Cloud

Get your backend ready in minutes

Use a managed database, storage, APIs, and admin dashboard without setting up the infrastructure yourself.

Set Up My Backend