3.0 KiB
3.0 KiB
title, impact, impactDescription, tags
| title | impact | impactDescription | tags |
|---|---|---|---|
| Pick the Right Record Hook - Model vs Request vs Enrich | HIGH | Wrong hook = missing request context, double-fired logic, or leaked fields in realtime events | hooks, onRecordEnrich, onRecordRequest, model-hooks, extending |
Pick the Right Record Hook - Model vs Request vs Enrich
PocketBase v0.23+ splits record hooks into three families. Using the wrong one is the #1 source of "my hook doesn't fire" and "my hidden field still shows up in realtime events" bugs.
| Family | Examples | Fires for | Has request context? |
|---|---|---|---|
| Model hooks | OnRecordCreate, OnRecordAfterCreateSuccess, OnRecordValidate |
Any save path - Web API and cron jobs, custom commands, migrations, calls from other hooks | No - e.Record, e.App, no e.RequestInfo |
| Request hooks | OnRecordCreateRequest, OnRecordsListRequest, OnRecordViewRequest |
Only the built-in Web API endpoints | Yes - e.RequestInfo, e.Auth, HTTP headers/body |
| Enrich hook | OnRecordEnrich |
Every response serialization, including realtime SSE events and apis.enrichRecord |
Yes, via e.RequestInfo |
Incorrect (hiding a field in the request hook - leaks in realtime):
// ❌ Only runs for GET /api/collections/users/records/{id}.
// Realtime SSE subscribers still receive the "role" field.
app.OnRecordViewRequest("users").BindFunc(func(e *core.RecordRequestEvent) error {
e.Record.Hide("role")
return e.Next()
})
Correct (use OnRecordEnrich so realtime and list responses also hide the field):
app.OnRecordEnrich("users").BindFunc(func(e *core.RecordEnrichEvent) error {
e.Record.Hide("role")
// Add a computed field only for authenticated users
if e.RequestInfo.Auth != nil {
e.Record.WithCustomData(true) // required to attach non-schema data
e.Record.Set("isOwner", e.Record.Id == e.RequestInfo.Auth.Id)
}
return e.Next()
})
// JSVM
onRecordEnrich((e) => {
e.record.hide("role");
if (e.requestInfo.auth?.collection()?.name === "users") {
e.record.withCustomData(true);
e.record.set("computedScore",
e.record.get("score") * e.requestInfo.auth.get("base"));
}
e.next();
}, "users");
Selection guide:
- Need to mutate the record before any save (API, cron, migration, nested hook)? →
OnRecordCreate/OnRecordUpdate(pre-save) orOnRecord*Success(post-save). - Need access to HTTP headers, query params, or the authenticated client? →
OnRecord*Request. - Need to hide fields, redact values, or attach computed props on responses including realtime? →
OnRecordEnrich- this is the safest default for response shaping. - Need to validate before save? →
OnRecordValidate(proxy overOnModelValidate).
Reference: Go Record request hooks · JS Record model hooks