Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions docs/access-control/collections.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,9 @@ As your application becomes more complex, you may want to define your function i

```ts
import type { Access } from 'payload'
import type { Page } from '@/payload-types'

export const canReadPage: Access = ({ req: { user } }) => {
export const canReadPage: Access<Page> = ({ req: { user } }) => {
// Allow authenticated users
if (user) {
return true
Expand Down Expand Up @@ -187,8 +188,9 @@ As your application becomes more complex, you may want to define your function i

```ts
import type { Access } from 'payload'
import type { User } from '@/payload-types'

export const canUpdateUser: Access = ({ req: { user }, id }) => {
export const canUpdateUser: Access<User> = ({ req: { user }, id }) => {
// Allow users with a role of 'admin'
if (user.roles && user.roles.some((role) => role === 'admin')) {
return true
Expand Down Expand Up @@ -232,8 +234,9 @@ As your application becomes more complex, you may want to define your function i

```ts
import type { Access } from 'payload'
import type { Customer } from '@/payload-types'

export const canDeleteCustomer: Access = async ({ req, id }) => {
export const canDeleteCustomer: Access<Customer> = async ({ req, id }) => {
if (!id) {
// allow the admin UI to show controls to delete since it is indeterminate without the `id`
return true
Expand Down
40 changes: 35 additions & 5 deletions docs/hooks/collections.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,11 @@ Please do note that this does not run before client-side validation. If you rend

```ts
import type { CollectionBeforeValidateHook } from 'payload'
import type { Post } from '@/payload-types'

const beforeValidateHook: CollectionBeforeValidateHook = async ({ data }) => {
const beforeValidateHook: CollectionBeforeValidateHook<Post> = async ({
data, // Typed as Partial<Post>
}) => {
return data
}
```
Expand All @@ -123,8 +126,11 @@ Immediately before validation, beforeChange hooks will run during create and upd

```ts
import type { CollectionBeforeChangeHook } from 'payload'
import type { Post } from '@/payload-types'

const beforeChangeHook: CollectionBeforeChangeHook = async ({ data }) => {
const beforeChangeHook: CollectionBeforeChangeHook<Post> = async ({
data, // Typed as Partial<Post>
}) => {
return data
}
```
Expand All @@ -146,8 +152,12 @@ After a document is created or updated, the `afterChange` hook runs. This hook i

```ts
import type { CollectionAfterChangeHook } from 'payload'
import type { Post } from '@/payload-types'

const afterChangeHook: CollectionAfterChangeHook = async ({ doc }) => {
const afterChangeHook: CollectionAfterChangeHook<Post> = async ({
doc, // Typed as Post
previousDoc, // Typed as Post
}) => {
return doc
}
```
Expand All @@ -170,8 +180,11 @@ Runs before `find` and `findByID` operations are transformed for output by `afte

```ts
import type { CollectionBeforeReadHook } from 'payload'
import type { Post } from '@/payload-types'

const beforeReadHook: CollectionBeforeReadHook = async ({ doc }) => {
const beforeReadHook: CollectionBeforeReadHook<Post> = async ({
doc, // Typed as Post
}) => {
return doc
}
```
Expand All @@ -192,8 +205,11 @@ Runs as the last step before documents are returned. Flattens locales, hides pro

```ts
import type { CollectionAfterReadHook } from 'payload'
import type { Post } from '@/payload-types'

const afterReadHook: CollectionAfterReadHook = async ({ doc }) => {
const afterReadHook: CollectionAfterReadHook<Post> = async ({
doc, // Typed as Post
}) => {
return doc
}
```
Expand Down Expand Up @@ -497,3 +513,17 @@ import type {
CollectionMeHook,
} from 'payload'
```

You can also pass a generic type to each hook for strongly-typed `doc`, `previousDoc`, and `data` properties:

```ts
import type { CollectionAfterChangeHook } from 'payload'
import type { Post } from '@/payload-types'

const afterChangeHook: CollectionAfterChangeHook<Post> = async ({
doc, // Typed as Post
previousDoc, // Typed as Post
}) => {
return doc
}
```
55 changes: 55 additions & 0 deletions docs/hooks/fields.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,58 @@ const exampleFieldHook: ExampleFieldHook = (args) => {
return value // should return a string as typed above, undefined, or null
}
```

### Practical Example with Generated Types

Here's a real-world example using generated Payload types:

```ts
import type { FieldHook } from 'payload'
import type { Post } from '@/payload-types'

// Hook for a text field in a Post collection
type PostTitleHook = FieldHook<Post, string, Post>

const slugifyTitle: PostTitleHook = ({
value,
data,
siblingData,
originalDoc,
}) => {
// value is typed as string | undefined
// data is typed as Partial<Post>
// siblingData is typed as Partial<Post>
// originalDoc is typed as Post | undefined

// Generate slug from title if not provided
if (!siblingData.slug && value) {
const slug = value
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-')

return value
}

return value
}

// Hook for a relationship field
type PostAuthorHook = FieldHook<Post, string | number, Post>

const setDefaultAuthor: PostAuthorHook = ({ value, req }) => {
// value is typed as string | number | undefined
// Set current user as author if not provided
if (!value && req.user) {
return req.user.id
}

return value
}
```

<Banner type="success">
**Tip:** When defining field hooks, use the three generic parameters for full
type safety: document type, field value type, and sibling data type. This
provides autocomplete and type checking for all hook arguments.
</Banner>
46 changes: 33 additions & 13 deletions docs/hooks/globals.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@ Please do note that this does not run before client-side validation. If you rend

```ts
import type { GlobalBeforeValidateHook } from 'payload'
import type { SiteSettings } from '@/payload-types'

const beforeValidateHook: GlobalBeforeValidateHook = async ({
data,
const beforeValidateHook: GlobalBeforeValidateHook<SiteSettings> = async ({
data, // Typed as Partial<SiteSettings>
req,
originalDoc,
originalDoc, // Typed as SiteSettings
}) => {
return data
}
Expand All @@ -112,11 +113,12 @@ Immediately following validation, `beforeChange` hooks will run within the `upda

```ts
import type { GlobalBeforeChangeHook } from 'payload'
import type { SiteSettings } from '@/payload-types'

const beforeChangeHook: GlobalBeforeChangeHook = async ({
data,
const beforeChangeHook: GlobalBeforeChangeHook<SiteSettings> = async ({
data, // Typed as Partial<SiteSettings>
req,
originalDoc,
originalDoc, // Typed as SiteSettings
}) => {
return data
}
Expand All @@ -138,13 +140,14 @@ After a global is updated, the `afterChange` hook runs. Use this hook to purge c

```ts
import type { GlobalAfterChangeHook } from 'payload'
import type { SiteSettings } from '@/payload-types'

const afterChangeHook: GlobalAfterChangeHook = async ({
doc,
previousDoc,
const afterChangeHook: GlobalAfterChangeHook<SiteSettings> = async ({
doc, // Typed as SiteSettings
previousDoc, // Typed as SiteSettings
req,
}) => {
return data
return doc
}
```

Expand Down Expand Up @@ -187,12 +190,15 @@ Runs as the last step before a global is returned. Flattens locales, hides prote

```ts
import type { GlobalAfterReadHook } from 'payload'
import type { SiteSettings } from '@/payload-types'

const afterReadHook: GlobalAfterReadHook = async ({
doc,
const afterReadHook: GlobalAfterReadHook<SiteSettings> = async ({
doc, // Typed as SiteSettings
req,
findMany,
}) => {...}
}) => {
return doc
}
```

The following arguments are provided to the `afterRead` hook:
Expand All @@ -219,3 +225,17 @@ import type {
GlobalAfterReadHook,
} from 'payload'
```

You can also pass a generic type to each hook for strongly-typed `doc`, `previousDoc`, and `data` properties:

```ts
import type { GlobalAfterChangeHook } from 'payload'
import type { SiteSettings } from '@/payload-types'

const afterChangeHook: GlobalAfterChangeHook<SiteSettings> = async ({
doc, // Typed as SiteSettings
previousDoc, // Typed as SiteSettings
}) => {
return doc
}
```