Collection hooks
Change document values before saving and choose when collection hooks run during creates, reads, updates, and deletes.
Collection hooks run Go functions when someone creates, reads, updates, or deletes a document. Use them when your code needs the whole document: record the editor, calculate several values, or save a related record. They run for the admin, REST, the SDK, and the local Go API.
Add hooks to the collection’s Hooks property. Each function
receives ridu.HookContext and returns an error. Returning nil
lets the operation continue. Ridu waits for each hook before moving to the next stage.
Record who edited a document
Use BeforeChange to change document values before saving. This helper records the signed-in
user’s ID on creates and updates:
package content
import (
"github.com/riducms/ridu"
"github.com/riducms/ridu/operation"
"github.com/riducms/ridu/store"
)
func recordLastEditor(ctx ridu.HookContext) error {
// A nil actor means the request is anonymous.
if ctx.Actor == nil {
return nil
}
// Limit editor tracking to creates and ordinary updates.
if ctx.Operation != operation.Create &&
ctx.Operation != operation.Update {
return nil
}
// Mutate Data entries to include them in the same save.
ctx.Data["lastEditedBy"] = store.String(ctx.Actor.ID)
return nil
}Add a field to store the ID and register the helper on your collection:
package content
import (
"github.com/riducms/ridu"
"github.com/riducms/ridu/field"
)
var Articles = ridu.Collection{
Slug: "articles",
Fields: field.Fields{
field.Text("title").Required(),
field.Text("lastEditedBy"),
},
Hooks: ridu.CollectionHooks{
// Run before saving so the editor ID is stored with the article.
BeforeChange: []ridu.Hook{recordLastEditor},
},
}Add Articles to Config.Collections. Create or edit an article while signed in: the saved
lastEditedBy value should be your user ID. Anonymous writes leave it unchanged. To include
duplication, publishing, or unpublishing, add those operation values to the helper’s condition.
ctx.Data contains the values being saved. Change its entries to update the document; returning
nil keeps those changes. Use ctx.Original to compare with the saved document, or ctx.Actor
to read the signed-in user. See Hook context for the values available at
each stage and the difference between changing stored values and changing a response.
Choose when your hook runs
Hook names describe when they run. For example, BeforeValidate runs before Ridu checks field
values, and AfterCommit runs once the database has successfully saved the operation. If you add
several hooks to the same list, Ridu runs them in the order you declare them.
| Hook | When it runs | Use it to |
|---|---|---|
BeforeDuplicate |
After the source is copied | Clear values that should not be copied |
BeforeValidate |
Before built-in field checks | Trim whitespace or normalize submitted input |
BeforeChange |
After built-in checks, before custom validators | Calculate values or record the editor |
BeforeOperation |
Before final validation and storage | Make a final change or inspect an operation |
BeforeRead |
Before documents are read | Prepare data needed while reading |
BeforeDelete |
After the original document is loaded | Clean up related data before deletion |
AfterChange / AfterDelete |
After storage, before the transaction commits | Write related data that must succeed together |
AfterRead |
Before unreadable fields are removed | Change values returned to the caller |
AfterOperation |
After the operation, before commit | Run follow-up database work |
AfterError |
When an operation fails | Log the failure or record a metric |
AfterCommit |
After the transaction commits | Send email, call webhooks, or update a search index |
BeforeDuplicate, BeforeChange, and AfterChange are specific to changes. BeforeDelete
and AfterDelete run for deletion, including trash and permanent deletion. Restoring a trashed
document does not run AfterChange.
BeforeValidate, BeforeOperation, AfterOperation, and AfterCommit are shared stages: they
can run for reads and deletes too. Check ctx.Operation when a hook should work only on saves.
AfterRead runs whenever Ridu prepares a returned document, including create, update, and delete
responses. A list runs it for each returned document.
The order of a save
For an ordinary create or update, Ridu runs this sequence:
- Check collection access and load the saved document when updating.
- Run the collection’s
BeforeValidatehooks, then the fields’BeforeValidatehooks. - Apply field defaults to omitted values in new documents, objects, or rows, combine an update with saved values, and run built-in checks.
- Run collection hooks, then field hooks, for
BeforeChangeand thenBeforeOperation. - Recheck values and field permissions, run custom
.Validate(...)rules, and check relationships. - Save the document inside the database transaction.
- Run collection hooks, then field hooks, for
AfterChangeand thenAfterOperation. - Prepare the response, including computed fields. Run collection
AfterRead, then fieldAfterRead, and remove values the caller cannot read. - Commit the transaction, then run field
AfterCommithooks followed by collectionAfterCommithooks.
A duplicate adds BeforeDuplicate before BeforeValidate, after copying the source. A publish
or unpublish also runs the change hooks, with its own ctx.Operation value. A failed hook stops
that operation before later stages run; after-commit failures are different
because the database has already committed.
The important distinction is between built-in checks and your custom validators. BeforeChange
receives values that have passed the first built-in checks, but your .Validate(...) rules run
after write hooks. Put a rule that must accept or reject the final value in
custom validation.
For hooks in the same list, declaration order is execution order. Do not rely on the order of unrelated fields to coordinate several changes. Put that work in one collection hook.
Save related records or notify another service
An AfterChange hook still runs inside the transaction. Use it for database changes that must
succeed together, such as an audit entry. Use AfterCommit for email, webhooks, or a search index:
the document has been saved by then, so a later failure cannot undo it.
Transactions and errors shows how to save related records, handle failures, and send notifications after commit. For a change to just one field, see Field hooks.