diff --git a/wiki/payloadcms/_index.md b/wiki/payloadcms/_index.md index e0aacf2..96cc91c 100644 --- a/wiki/payloadcms/_index.md +++ b/wiki/payloadcms/_index.md @@ -88,6 +88,7 @@ | [[wiki/payloadcms/fields-ui\|UI Field]] | Presentational-only field — inject arbitrary React components into Admin Panel Edit/List views; no data stored; use for action buttons, context text, custom controls | raw/fields__ui.md | 2026-05-15 | | [[wiki/payloadcms/fields-upload\|Upload Field]] | Reference upload-enabled Collection docs as thumbnail — hasMany, polymorphic multi-collection, filterOptions, bi-directional via Join | raw/fields__upload.md | 2026-05-15 | | [[wiki/payloadcms/graphql-overview\|GraphQL API — Overview]] | Auto-generated GraphQL endpoint: queries/mutations for Collections, Globals, Preferences; playground, complexity limits, custom validation rules | raw/graphql__overview.md | 2026-05-15 | +| [[wiki/payloadcms/hooks-collections\|Collection Hooks]] | All 18 collection hook types (write/read/delete/auth lifecycle), `data` vs `originalDoc` gotcha, auth hooks, TypeScript generics | raw/hooks__collections.md | 2026-05-15 | | [[wiki/payloadcms/concepts-overview\|Core Concepts]] | Config, Collections, Globals, Fields, Hooks, Auth, Access Control, Admin Panel; Local/REST/GraphQL APIs; package structure | raw/getting-started__concepts.md | 2026-05-15 | | [[wiki/payloadcms/installation\|Installation]] | Requirements (Node 24+, Next.js 16.2.6+), create-payload-app quickstart, manual install steps, withPayload ESM config, tsconfig path alias | raw/getting-started__installation.md | 2026-05-15 | | [[wiki/payloadcms/graphql-extending\|GraphQL — Custom Queries and Mutations]] | Add custom GraphQL ops via `graphQL.queries`/`graphQL.mutations` in buildConfig; resolver signature, `depth: 0` gotcha, `buildPaginatedListType`, collection graphQL types | raw/graphql__extending.md | 2026-05-15 | diff --git a/wiki/payloadcms/hooks-collections.md b/wiki/payloadcms/hooks-collections.md new file mode 100644 index 0000000..c3d7f07 --- /dev/null +++ b/wiki/payloadcms/hooks-collections.md @@ -0,0 +1,160 @@ +--- +title: "Collection Hooks" +aliases: [collection-hooks, payload-collection-hooks] +tags: [payloadcms, hooks, collections, lifecycle, auth] +sources: [raw/hooks__collections.md] +created: 2026-05-15 +updated: 2026-05-15 +--- + +Collection Hooks run on Documents within a specific [[wiki/payloadcms/collection-config|Collection]], letting you execute custom logic at precise moments in the Document lifecycle. + +```ts +export const MyCollection: CollectionConfig = { + hooks: { + beforeOperation: [fn], + beforeValidate: [fn], + beforeChange: [fn], + afterChange: [fn], + beforeRead: [fn], + afterRead: [fn], + beforeDelete: [fn], + afterDelete: [fn], + afterOperation: [fn], + afterError: [fn], + // Auth-only: + beforeLogin: [fn], afterLogin: [fn], afterLogout: [fn], + afterRefresh: [fn], afterMe: [fn], afterForgotPassword: [fn], + refresh: [fn], me: [fn], + }, +} +``` + +Each hook accepts an **array** of synchronous or asynchronous functions. + +--- + +## Hook Reference + +### Write Lifecycle (create / update) + +| Hook | When | Typical use | +|------|------|-------------| +| `beforeOperation` | Before any operation starts | Modify operation args, early side-effects | +| `beforeValidate` | Before server validation | Format / enrich incoming data | +| `beforeChange` | After validation, before DB write | Data transformation, enforce business rules | +| `afterChange` | After DB write | Sync to CRM, recalculate aggregates | + +**Order:** `beforeOperation` → `beforeValidate` → client `validate` → server `validate` → `beforeChange` → DB write → `afterChange` → `afterOperation` + +### Read Lifecycle + +| Hook | When | Notes | +|------|------|-------| +| `beforeRead` | Before locale-flattening / hidden-field removal | Has access to **all locales** and hidden fields | +| `afterRead` | Last step before response | Locales flattened, protected fields removed | + +### Delete Lifecycle + +| Hook | Return value | +|------|-------------| +| `beforeDelete` | Discarded | +| `afterDelete` | Discarded | + +### Error Hook + +`afterError` — fires on any Payload application error. Use for Sentry/DataDog/email alerts. Can transform the result object and HTTP status code. + +--- + +## Key Arguments + +### `data` vs `originalDoc` + +> **`data` is a delta** — it only contains fields being changed, NOT the full document. + +```ts +// On UPDATE — get id from originalDoc, not data +const id = operation === 'update' ? originalDoc.id : undefined + +// On CREATE — id is not available until afterChange +``` + +| Hook | `data` | `originalDoc` | `doc` | +|------|--------|--------------|-------| +| `beforeValidate` | changed fields | full pre-change doc (update only) | — | +| `beforeChange` | changed fields | full pre-change doc (update only) | — | +| `afterChange` | changed fields | — | full resulting doc | +| `afterRead` | — | — | full resulting doc | + +### Common Arguments (all hooks) + +- **`collection`** — Collection config object +- **`context`** — custom pass-through between hooks (see [[wiki/payloadcms/hooks|Hooks overview]]) +- **`req`** — Web Request (mocked for [[wiki/payloadcms/local-api|Local API]]) +- **`operation`** — `'create' | 'update' | 'delete' | 'find' | ...` + +--- + +## Auth-Enabled Hooks + +For collections with `auth: true`. See [[wiki/payloadcms/authentication-overview|Authentication Overview]]. + +| Hook | Fires | Can modify | +|------|-------|-----------| +| `beforeLogin` | After credentials matched, before token | Return modified user or throw to deny login | +| `afterLogin` | After successful login | Return modified user | +| `afterLogout` | After logout | Side-effects only | +| `afterRefresh` | After token refresh | Side-effects only | +| `afterMe` | After `/me` request | Return modified response | +| `afterForgotPassword` | After forgot-password | Return value discarded | +| `refresh` | Replaces default refresh logic | Return value skips default operation | +| `me` | Replaces default me logic | Return value skips default operation | + +--- + +## TypeScript + +```ts +import type { + CollectionBeforeOperationHook, + CollectionBeforeValidateHook, + CollectionBeforeChangeHook, + CollectionAfterChangeHook, + CollectionAfterReadHook, + CollectionBeforeReadHook, + CollectionBeforeDeleteHook, + CollectionAfterDeleteHook, + CollectionBeforeLoginHook, + CollectionAfterLoginHook, + CollectionAfterLogoutHook, + CollectionAfterRefreshHook, + CollectionAfterMeHook, + CollectionAfterForgotPasswordHook, + CollectionRefreshHook, + CollectionMeHook, +} from 'payload' + +// Generic typing for doc/data +const afterChangeHook: CollectionAfterChangeHook = async ({ doc, previousDoc }) => { + return doc +} +``` + +--- + +## Key Takeaways + +- **`data` is always a partial delta** — never assume `id` or unchanged fields are present; use `originalDoc` on updates, `afterChange` for the `id` on creates +- **`beforeChange` data is unvalidated** — treat it as raw user input; required fields may be missing +- **`beforeRead`** sees all locales and hidden fields before Payload strips them — use it for internal transforms +- **`afterRead`** sees the cleaned, user-visible document — use it for computed props exposed to clients +- Auth hooks (`beforeLogin`, `refresh`, `me`) can **replace default behavior** by returning early — powerful but be careful +- `afterError` hook is the right place for Sentry / alerting integration, not `try/catch` in every hook +- Field-level hooks are an alternative for isolating logic — see [[wiki/payloadcms/hooks|Hooks Overview]] + +--- + +## Sources + +- `raw/hooks__collections.md` (source: https://payloadcms.com/docs/hooks/collections)