payload

Payload CMS Application Development

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "payload" with this command: npx skills add microck/ordinary-claude-skills/microck-ordinary-claude-skills-payload

Payload CMS Application Development

Payload is a Next.js native CMS with TypeScript-first architecture, providing admin panel, database management, REST/GraphQL APIs, authentication, and file storage.

Quick Reference

Task Solution Details

Auto-generate slugs slugField()

FIELDS.md#slug-field-helper

Restrict content by user Access control with query ACCESS-CONTROL.md#row-level-security-with-complex-queries

Local API user ops user

  • overrideAccess: false

QUERIES.md#access-control-in-local-api

Draft/publish workflow versions: { drafts: true }

COLLECTIONS.md#versioning--drafts

Computed fields virtual: true with afterRead FIELDS.md#virtual-fields

Conditional fields admin.condition

FIELDS.md#conditional-fields

Custom field validation validate function FIELDS.md#validation

Filter relationship list filterOptions on field FIELDS.md#relationship

Select specific fields select parameter QUERIES.md#field-selection

Auto-set author/dates beforeChange hook HOOKS.md#collection-hooks

Prevent hook loops req.context check HOOKS.md#context

Cascading deletes beforeDelete hook HOOKS.md#collection-hooks

Geospatial queries point field with near /within

FIELDS.md#point-geolocation

Reverse relationships join field type FIELDS.md#join-fields

Next.js revalidation Context control in afterChange HOOKS.md#nextjs-revalidation-with-context-control

Query by relationship Nested property syntax QUERIES.md#nested-properties

Complex queries AND/OR logic QUERIES.md#andor-logic

Transactions Pass req to operations ADAPTERS.md#threading-req-through-operations

Background jobs Jobs queue with tasks ADVANCED.md#jobs-queue

Custom API routes Collection custom endpoints ADVANCED.md#custom-endpoints

Cloud storage Storage adapter plugins ADAPTERS.md#storage-adapters

Multi-language localization config + localized: true

ADVANCED.md#localization

Create plugin (options) => (config) => Config

PLUGIN-DEVELOPMENT.md#plugin-architecture

Plugin package setup Package structure with SWC PLUGIN-DEVELOPMENT.md#plugin-package-structure

Add fields to collection Map collections, spread fields PLUGIN-DEVELOPMENT.md#adding-fields-to-collections

Plugin hooks Preserve existing hooks in array PLUGIN-DEVELOPMENT.md#adding-hooks

Check field type Type guard functions FIELD-TYPE-GUARDS.md

Quick Start

npx create-payload-app@latest my-app cd my-app pnpm dev

Minimal Config

import { buildConfig } from 'payload' import { mongooseAdapter } from '@payloadcms/db-mongodb' import { lexicalEditor } from '@payloadcms/richtext-lexical' import path from 'path' import { fileURLToPath } from 'url'

const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename)

export default buildConfig({ admin: { user: 'users', importMap: { baseDir: path.resolve(dirname), }, }, collections: [Users, Media], editor: lexicalEditor(), secret: process.env.PAYLOAD_SECRET, typescript: { outputFile: path.resolve(dirname, 'payload-types.ts'), }, db: mongooseAdapter({ url: process.env.DATABASE_URI, }), })

Essential Patterns

Basic Collection

import type { CollectionConfig } from 'payload'

export const Posts: CollectionConfig = { slug: 'posts', admin: { useAsTitle: 'title', defaultColumns: ['title', 'author', 'status', 'createdAt'], }, fields: [ { name: 'title', type: 'text', required: true }, { name: 'slug', type: 'text', unique: true, index: true }, { name: 'content', type: 'richText' }, { name: 'author', type: 'relationship', relationTo: 'users' }, ], timestamps: true, }

For more collection patterns (auth, upload, drafts, live preview), see COLLECTIONS.md.

Common Fields

// Text field { name: 'title', type: 'text', required: true }

// Relationship { name: 'author', type: 'relationship', relationTo: 'users', required: true }

// Rich text { name: 'content', type: 'richText', required: true }

// Select { name: 'status', type: 'select', options: ['draft', 'published'], defaultValue: 'draft' }

// Upload { name: 'image', type: 'upload', relationTo: 'media' }

For all field types (array, blocks, point, join, virtual, conditional, etc.), see FIELDS.md.

Hook Example

export const Posts: CollectionConfig = { slug: 'posts', hooks: { beforeChange: [ async ({ data, operation }) => { if (operation === 'create') { data.slug = slugify(data.title) } return data }, ], }, fields: [{ name: 'title', type: 'text' }], }

For all hook patterns, see HOOKS.md. For access control, see ACCESS-CONTROL.md.

Access Control with Type Safety

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

// Type-safe access control export const adminOnly: Access = ({ req }) => { const user = req.user as User return user?.roles?.includes('admin') || false }

// Row-level access control export const ownPostsOnly: Access = ({ req }) => { const user = req.user as User if (!user) return false if (user.roles?.includes('admin')) return true

return { author: { equals: user.id }, } }

Query Example

// Local API const posts = await payload.find({ collection: 'posts', where: { status: { equals: 'published' }, 'author.name': { contains: 'john' }, }, depth: 2, limit: 10, sort: '-createdAt', })

// Query with populated relationships const post = await payload.findByID({ collection: 'posts', id: '123', depth: 2, // Populates relationships (default is 2) }) // Returns: { author: { id: "user123", name: "John" } }

// Without depth, relationships return IDs only const post = await payload.findByID({ collection: 'posts', id: '123', depth: 0, }) // Returns: { author: "user123" }

For all query operators and REST/GraphQL examples, see QUERIES.md.

Getting Payload Instance

// In API routes (Next.js) import { getPayload } from 'payload' import config from '@payload-config'

export async function GET() { const payload = await getPayload({ config })

const posts = await payload.find({ collection: 'posts', })

return Response.json(posts) }

// In Server Components import { getPayload } from 'payload' import config from '@payload-config'

export default async function Page() { const payload = await getPayload({ config }) const { docs } = await payload.find({ collection: 'posts' })

return <div>{docs.map(post => <h1 key={post.id}>{post.title}</h1>)}</div> }

Security Pitfalls

  1. Local API Access Control (CRITICAL)

By default, Local API operations bypass ALL access control, even when passing a user.

// ❌ SECURITY BUG: Passes user but ignores their permissions await payload.find({ collection: 'posts', user: someUser, // Access control is BYPASSED! })

// ✅ SECURE: Actually enforces the user's permissions await payload.find({ collection: 'posts', user: someUser, overrideAccess: false, // REQUIRED for access control })

When to use each:

  • overrideAccess: true (default) - Server-side operations you trust (cron jobs, system tasks)

  • overrideAccess: false

  • When operating on behalf of a user (API routes, webhooks)

See QUERIES.md#access-control-in-local-api.

  1. Transaction Failures in Hooks

Nested operations in hooks without req break transaction atomicity.

// ❌ DATA CORRUPTION RISK: Separate transaction hooks: { afterChange: [ async ({ doc, req }) => { await req.payload.create({ collection: 'audit-log', data: { docId: doc.id }, // Missing req - runs in separate transaction! }) }, ] }

// ✅ ATOMIC: Same transaction hooks: { afterChange: [ async ({ doc, req }) => { await req.payload.create({ collection: 'audit-log', data: { docId: doc.id }, req, // Maintains atomicity }) }, ] }

See ADAPTERS.md#threading-req-through-operations.

  1. Infinite Hook Loops

Hooks triggering operations that trigger the same hooks create infinite loops.

// ❌ INFINITE LOOP hooks: { afterChange: [ async ({ doc, req }) => { await req.payload.update({ collection: 'posts', id: doc.id, data: { views: doc.views + 1 }, req, }) // Triggers afterChange again! }, ] }

// ✅ SAFE: Use context flag hooks: { afterChange: [ async ({ doc, req, context }) => { if (context.skipHooks) return

  await req.payload.update({
    collection: 'posts',
    id: doc.id,
    data: { views: doc.views + 1 },
    context: { skipHooks: true },
    req,
  })
},

] }

See HOOKS.md#context.

Project Structure

src/ ├── app/ │ ├── (frontend)/ │ │ └── page.tsx │ └── (payload)/ │ └── admin/[[...segments]]/page.tsx ├── collections/ │ ├── Posts.ts │ ├── Media.ts │ └── Users.ts ├── globals/ │ └── Header.ts ├── components/ │ └── CustomField.tsx ├── hooks/ │ └── slugify.ts └── payload.config.ts

Type Generation

// payload.config.ts export default buildConfig({ typescript: { outputFile: path.resolve(dirname, 'payload-types.ts'), }, // ... })

// Usage import type { Post, User } from '@/payload-types'

Reference Documentation

  • FIELDS.md - All field types, validation, admin options

  • FIELD-TYPE-GUARDS.md - Type guards for runtime field type checking and narrowing

  • COLLECTIONS.md - Collection configs, auth, upload, drafts, live preview

  • HOOKS.md - Collection hooks, field hooks, context patterns

  • ACCESS-CONTROL.md - Collection, field, global access control, RBAC, multi-tenant

  • ACCESS-CONTROL-ADVANCED.md - Context-aware, time-based, subscription-based access, factory functions, templates

  • QUERIES.md - Query operators, Local/REST/GraphQL APIs

  • ENDPOINTS.md - Custom API endpoints: authentication, helpers, request/response patterns

  • ADAPTERS.md - Database, storage, email adapters, transactions

  • ADVANCED.md - Authentication, jobs, endpoints, components, plugins, localization

  • PLUGIN-DEVELOPMENT.md - Plugin architecture, monorepo structure, patterns, best practices

Resources

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

moon-dev-trading-agents

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

shopify-app-dev

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

aws-cdk-development

No summary provided by upstream source.

Repository SourceNeeds Review