| title |
aliases |
tags |
sources |
created |
updated |
| Payload CMS — Collection Access Control |
| payload-access-control |
| payload-collections-permissions |
|
| payload-cms |
| access-control |
| permissions |
| nextjs |
| cms |
|
| raw/access-control__collections.md |
|
2026-05-15 |
2026-05-15 |
Payload CMS — Collection Access Control
Defines which users can create, read, update, or delete documents within a wiki/tech-patterns/payload-cms-installation. Controlled via the access property on CollectionConfig.
Config Structure
import type { CollectionConfig } from 'payload'
export const Pages: CollectionConfig = {
slug: 'pages',
access: {
create: ({ req: { user }, data }) => Boolean(user),
read: ({ req: { user } }) => Boolean(user),
update: ({ req: { user } }) => Boolean(user),
delete: ({ req: { user } }) => Boolean(user),
// Auth-enabled collections only:
admin: ({ req: { user } }) => Boolean(user),
unlock: ({ req: { user } }) => Boolean(user),
// Versions-enabled collections only:
readVersions: ({ req: { user } }) => Boolean(user),
},
}
Access Functions Reference
| Function |
Operation |
Extra args |
create |
POST /api/:collection |
req, data |
read |
GET /api/:collection, findByID |
req, id |
update |
PATCH /api/:collection/:id |
req, id, data |
delete |
DELETE /api/:collection/:id |
req, id, data |
admin |
Admin Panel access |
req |
unlock |
Unlock locked-out users |
req |
readVersions |
Version history tab |
req |
All functions receive req which contains req.user (the authenticated user or null).
Return Values
Each function can return:
true — full access granted
false — access denied
- Query constraint — access limited to documents matching the where-clause (works for
read, update, delete, readVersions)
Common Patterns
Public read, authenticated write
read: ({ req: { user } }) => {
if (user) return true
return { isPublic: { equals: true } } // guest users see only public docs
},
create: ({ req: { user } }) => Boolean(user),
Role-based update
update: ({ req: { user }, id }) => {
if (user?.roles?.includes('admin')) return true
return user?.id === id // users can only update themselves
},
Delete guard — check foreign references
delete: async ({ req, id }) => {
if (!id) return true // let Admin UI show controls without id
const result = await req.payload.find({
collection: 'contracts',
where: { customer: { equals: id } },
limit: 0, depth: 0,
})
return result.totalDocs === 0 // block delete if contracts exist
},
Soft-delete vs hard-delete (Trash-enabled)
delete: ({ req: { user }, data }) => {
const isSoftDelete = Boolean(data?.deletedAt)
if (isSoftDelete) return Boolean(user) // any auth user can trash
return user?.roles?.includes('admin') ?? false // only admins hard-delete
},
Tips & Gotchas
- Returning a Query on
readVersions applies the constraint to the _versions collection, not the original — be aware of field name differences.
- Draft publishing control: use
update returning a query on _status field to restrict who can publish drafts.
delete without id (bulk/Admin UI list view) — id is undefined; returning true lets the UI render delete controls even before knowing which document is targeted.
- Extract complex functions into a separate
access/ file to keep CollectionConfig readable. Import with import type { Access } from 'payload'.
admin access only applies to Auth-enabled collections (the collection used as the admin user source).
Key Takeaways
- Access is per-operation —
create, read, update, delete are always available; admin/unlock require Auth; readVersions requires Versions.
- Return
true/false for binary access or a Query object for row-level security.
req.user is null for unauthenticated requests — always guard with Boolean(user) or a null-check.
- Complex access logic belongs in a separate file using the
Access<CollectionType> generic type.
delete receives data.deletedAt for Trash-enabled collections — use it to distinguish soft vs permanent deletes.
Related
Sources