Skip to content

Commit ba9605e

Browse files
authored
docs: clarify beforeValidate and beforeChange hook data behavior (#15300)
- Updated originalDoc descriptions in both beforeValidate and beforeChange hooks - Added warning banners explaining partial data behavior - Improved beforeChange example showing practical validation with operation checking
1 parent 126f713 commit ba9605e

2 files changed

Lines changed: 53 additions & 8 deletions

File tree

docs/hooks/collections.mdx

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ const beforeOperationHook: CollectionBeforeOperationHook = async ({
8282
The following arguments are provided to the `beforeOperation` hook:
8383

8484
| Option | Description |
85-
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------|
85+
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
8686
| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |
8787
| **`context`** | Custom context passed between Hooks. [More details](./context). |
8888
| **`operation`** | The name of the operation that this hook is running within. |
@@ -117,20 +117,40 @@ The following arguments are provided to the `beforeValidate` hook:
117117
| **`context`** | Custom context passed between Hooks. [More details](./context). |
118118
| **`data`** | The incoming data passed through the operation. |
119119
| **`operation`** | The name of the operation that this hook is running within. |
120-
| **`originalDoc`** | The Document before changes are applied. |
120+
| **`originalDoc`** | The full document before changes are applied. Present on updates; undefined on creates. Use this to read the document id and any unchanged fields. |
121121
| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |
122122

123+
**Why isn’t id in data?**
124+
125+
`data` only represents the delta you’re saving. Read `originalDoc.id` during updates, and prefer `afterChange` if you need the `id` during creates
126+
127+
<Banner type="warning">
128+
On update operations, data contains only the fields being changed. It may omit
129+
id and any unchanged fields. Use originalDoc to read existing values. On
130+
create, data is the new submission and the document id is not yet available at
131+
this stage.
132+
</Banner>
133+
123134
### beforeChange
124135

125136
Immediately before validation, beforeChange hooks will run during create and update operations. At this stage, the data should be treated as unvalidated user input. There is no guarantee that required fields exist or that fields are in the correct format. As such, using this data for side effects requires manual validation. You can optionally modify the shape of the data to be saved.
126137

127138
```ts
128139
import type { CollectionBeforeChangeHook } from 'payload'
129-
import type { Post } from '@/payload-types'
130140

131-
const beforeChangeHook: CollectionBeforeChangeHook<Post> = async ({
132-
data, // Typed as Partial<Post>
141+
export const requireTitleOnUpdate: CollectionBeforeChangeHook = async ({
142+
data, // Partial<T> — changed fields only
143+
originalDoc, // Full doc before changes (defined on update)
144+
operation, // 'create' | 'update'
133145
}) => {
146+
// Need the id? Don't expect it in `data`.
147+
const id = operation === 'update' ? originalDoc.id : undefined
148+
149+
// Example: enforce that title cannot be cleared on update
150+
if (operation === 'update' && 'title' in (data ?? {}) && !data.title) {
151+
throw new Error(`Document ${id} must have a title`)
152+
}
153+
134154
return data
135155
}
136156
```
@@ -143,9 +163,20 @@ The following arguments are provided to the `beforeChange` hook:
143163
| **`context`** | Custom context passed between hooks. [More details](./context). |
144164
| **`data`** | The incoming data passed through the operation. |
145165
| **`operation`** | The name of the operation that this hook is running within. |
146-
| **`originalDoc`** | The Document before changes are applied. |
166+
| **`originalDoc`** | The full document before changes are applied. Present on updates; undefined on creates. Use this to read the document id and any unchanged fields. |
147167
| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |
148168

169+
**Why isn’t id in data?**
170+
171+
`data` only represents the delta you’re saving. Read `originalDoc.id` during updates, and prefer `afterChange` if you need the `id` during creates
172+
173+
<Banner type="warning">
174+
On update operations, data contains only the fields being changed. It may omit
175+
id and any unchanged fields. Use originalDoc to read existing values. On
176+
create, data is the new submission and the document id is not yet available at
177+
this stage.
178+
</Banner>
179+
149180
### afterChange
150181

151182
After a document is created or updated, the `afterChange` hook runs. This hook is helpful to recalculate statistics such as total sales within a global, syncing user profile changes to a CRM, and more.

docs/hooks/globals.mdx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,16 @@ The following arguments are provided to the `beforeValidate` hook:
104104
| **`global`** | The [Global](../configuration/globals) in which this Hook is running against. |
105105
| **`context`** | Custom context passed between Hooks. [More details](./context). |
106106
| **`data`** | The incoming data passed through the operation. |
107-
| **`originalDoc`** | The Document before changes are applied. |
107+
| **`originalDoc`** | The full document before changes are applied. Present on updates; undefined on creates. Use this to read the document id and any unchanged fields. |
108108
| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |
109109

110+
<Banner type="warning">
111+
On update operations, data contains only the fields being changed. It may omit
112+
id and any unchanged fields. Use originalDoc to read existing values. On
113+
create, data is the new submission and the document id is not yet available at
114+
this stage.
115+
</Banner>
116+
110117
### beforeChange
111118

112119
Immediately following validation, `beforeChange` hooks will run within the `update` operation. At this stage, you can be confident that the data that will be saved to the document is valid in accordance to your field validations. You can optionally modify the shape of data to be saved.
@@ -131,9 +138,16 @@ The following arguments are provided to the `beforeChange` hook:
131138
| **`global`** | The [Global](../configuration/globals) in which this Hook is running against. |
132139
| **`context`** | Custom context passed between hooks. [More details](./context). |
133140
| **`data`** | The incoming data passed through the operation. |
134-
| **`originalDoc`** | The Document before changes are applied. |
141+
| **`originalDoc`** | The full document before changes are applied. Present on updates; undefined on creates. Use this to read the document id and any unchanged fields. |
135142
| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |
136143

144+
<Banner type="warning">
145+
On update operations, data contains only the fields being changed. It may omit
146+
id and any unchanged fields. Use originalDoc to read existing values. On
147+
create, data is the new submission and the document id is not yet available at
148+
this stage.
149+
</Banner>
150+
137151
### afterChange
138152

139153
After a global is updated, the `afterChange` hook runs. Use this hook to purge caches of your applications, sync site data to CRMs, and more.

0 commit comments

Comments
 (0)