JSTAcademy
0 XP
Dashboard
Crash Courses
Access Control
10 min
Basic+150 XP
Crash Courses · Basic

Access Control

Secure Payload collections with function-based access control.
10 min read+150 XP on completionCert: Payload CMS Crash Course
Tap any word in the text below to start reading from there.

Access Control

Payload access control uses functions that receive the user and document, return a boolean or a where constraint.

Collection-level Access

// collections/Posts.ts
import type { CollectionConfig, Access } from 'payload'

const isAdmin: Access = ({ req: { user } }) => {
  return user?.role === 'admin'
}

const isAdminOrPublished: Access = ({ req: { user } }) => {
  if (user?.role === 'admin') return true
  // Non-admins can only read published posts
  return { status: { equals: 'published' } }
}

const isAdminOrSelf: Access = ({ req: { user }, id }) => {
  if (user?.role === 'admin') return true
  return user?.id === id  // can only update/delete own document
}

export const Posts: CollectionConfig = {
  slug: 'posts',
  access: {
    create: isAdmin,           // only admins create posts
    read: isAdminOrPublished,  // public reads only published
    update: isAdminOrSelf,     // admins + own document
    delete: isAdmin,
  },
  fields: [
    {
      name: 'email',
      type: 'email',
      access: {
        read: isAdmin,  // field-level: only admins see email
      },
    },
    // ...
  ],
}

Where Constraints in Access Control

// Return a query filter instead of boolean
const canReadOwnOrPublic: Access = ({ req: { user } }) => {
  if (!user) {
    // Unauthenticated — only public posts
    return { status: { equals: 'published' } }
  }

  if (user.role === 'admin') return true

  // Authenticated users see own drafts + all published
  return {
    or: [
      { author: { equals: user.id } },
      { status: { equals: 'published' } },
    ],
  }
}

Users Collection with Roles

export const Users: CollectionConfig = {
  slug: 'users',
  auth: true,  // enables built-in auth
  fields: [
    {
      name: 'role',
      type: 'select',
      options: ['admin', 'editor', 'user'],
      defaultValue: 'user',
      access: { update: isAdmin },  // only admins can change roles
    },
    { name: 'displayName', type: 'text' },
  ],
}
0%