115 lines
3.9 KiB
Markdown
115 lines
3.9 KiB
Markdown
---
|
|
title: "Hooks — Context (req.context)"
|
|
aliases: [payload-context, hooks-context, req-context]
|
|
tags: [payloadcms, hooks, context, typescript]
|
|
sources: [raw/hooks__context.md]
|
|
created: 2026-05-15
|
|
updated: 2026-05-15
|
|
---
|
|
|
|
## Overview
|
|
|
|
`req.context` is a plain object that persists across the **entire lifecycle of a single request** — available in every hook, middleware, and Local API call for that request. Use it to share data between hooks without extra DB/API calls.
|
|
|
|
## When To Use
|
|
|
|
| Problem | Context Solution |
|
|
|---------|-----------------|
|
|
| Need same 3rd-party data in `beforeChange` and `afterChange` | Fetch once in `beforeChange`, store on `context`, read in `afterChange` |
|
|
| `afterChange` calls `payload.update()` on the same collection → infinite loop | Set a flag (`context.triggerAfterChange = false`) and guard with early return |
|
|
| Pass extra data to Local API without adding spurious fields | Set on `req.context`, pass `context` option to `payload.create()` / `payload.update()` |
|
|
| Share values between hooks and custom middleware/endpoints | Hooks set context; `postMiddleware` reads it |
|
|
|
|
## How To Use
|
|
|
|
### Passing Data Between Hooks
|
|
|
|
```ts
|
|
const Customer: CollectionConfig = {
|
|
slug: 'customers',
|
|
hooks: {
|
|
beforeChange: [
|
|
async ({ context, data }) => {
|
|
// fetch once, store on context
|
|
context.customerData = await fetchCustomerData(data.customerID)
|
|
return { ...data, name: context.customerData.name }
|
|
},
|
|
],
|
|
afterChange: [
|
|
async ({ context }) => {
|
|
// reuse — no second fetch
|
|
if (context.customerData.contacted === false) {
|
|
createTodo('Call Customer', context.customerData)
|
|
}
|
|
},
|
|
],
|
|
},
|
|
}
|
|
```
|
|
|
|
### Preventing Infinite Loops
|
|
|
|
**Bad** — `afterChange` calling `payload.update()` on the same collection triggers itself:
|
|
|
|
```ts
|
|
afterChange: [
|
|
async ({ doc, req }) => {
|
|
await req.payload.update({ collection: 'customers', id: doc.id, data: { ... } })
|
|
// ☠️ infinite loop
|
|
},
|
|
],
|
|
```
|
|
|
|
**Fixed** — use a boolean flag passed through `context`:
|
|
|
|
```ts
|
|
afterChange: [
|
|
async ({ context, doc, req }) => {
|
|
if (context.triggerAfterChange === false) return // guard
|
|
await req.payload.update({
|
|
collection: 'customers',
|
|
id: doc.id,
|
|
data: { ... },
|
|
context: { triggerAfterChange: false }, // flag for next invocation
|
|
})
|
|
},
|
|
],
|
|
```
|
|
|
|
## TypeScript — Module Augmentation
|
|
|
|
Default type is `{ [key: string]: unknown }`. For strict typing, augment `RequestContext`:
|
|
|
|
```ts
|
|
// any .ts or .d.ts file
|
|
declare module 'payload' {
|
|
export interface RequestContext {
|
|
customerData?: CustomerData
|
|
triggerAfterChange?: boolean
|
|
}
|
|
}
|
|
```
|
|
|
|
- Uses [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)
|
|
- Gets autocomplete on `context.*` everywhere in hooks
|
|
- Wrong augmentation syntax breaks all types — copy the exact pattern above
|
|
|
|
## Key Takeaways
|
|
|
|
- `req.context` persists for the full request lifecycle; set in any hook, read in any later hook
|
|
- Primary use cases: **data sharing** (avoid duplicate fetches) and **infinite loop prevention** (flag pattern)
|
|
- Pass custom context into Local API calls via the `context` option on `payload.create()` / `payload.update()`
|
|
- Augment `RequestContext` interface for type-safe context properties across the project
|
|
- The flag pattern for loops: check flag → early return → pass `context: { flag: false }` in the nested update
|
|
|
|
## Related
|
|
|
|
- [[wiki/payloadcms/hooks|Hooks Overview]] — hook types and lifecycle
|
|
- [[wiki/payloadcms/hooks-collections|Collection Hooks]] — all 18 collection hook signatures
|
|
- [[wiki/payloadcms/local-api|Local API]] — `payload.update()` / `payload.create()` with context option
|
|
- [[wiki/payloadcms/database-transactions|Database Transactions]] — another request-scoped mechanism (`req`)
|
|
|
|
## Sources
|
|
|
|
- `raw/hooks__context.md` — compiled 2026-05-15
|
|
- Official docs: https://payloadcms.com/docs/hooks/context
|