Initial commit

This commit is contained in:
2026-04-17 23:26:01 +00:00
commit 2ea4ca5d52
409 changed files with 63459 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
---
title: Pick the Right Record Hook - Model vs Request vs Enrich
impact: HIGH
impactDescription: Wrong hook = missing request context, double-fired logic, or leaked fields in realtime events
tags: 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):**
```go
// ❌ 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):**
```go
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()
})
```
```javascript
// 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) or `OnRecord*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 over `OnModelValidate`).
Reference: [Go Record request hooks](https://pocketbase.io/docs/go-event-hooks/#record-crud-request-hooks) · [JS Record model hooks](https://pocketbase.io/docs/js-event-hooks/#record-model-hooks)