Install
$ agentstack add skill-0xdarkmatter-claude-mods-payloadcms-ops ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Payload CMS Operations
Authoritative reference for Payload 3.x — the Next.js-native, TypeScript-first headless CMS. Payload 3 installs into a Next.js (App Router) app and gives you an auto-generated admin panel, REST + GraphQL APIs, a typed Local API, authentication, access control, file storage, and live preview — one open-source TypeScript codebase.
> Version note (verified against payloadcms.com/docs, 2026-06): Payload 3 is the Next.js fullstack framework — there is no standalone Express server anymore. The config lives at src/payload.config.ts; Payload mounts into the Next App Router via the installed (payload) route group. Don't ship Payload 2.x "standalone Express app" guidance.
Architecture at a glance
| Piece | What it is | |-------|-----------| | payload.config.ts | Single source of truth: collections, globals, db adapter, plugins, admin, auth | | Collections | Repeatable document groups (Posts, Users, Media) — the core building block | | Globals | Singletons (one document) — site settings, header/footer nav | | Fields | Compose document shape; also drive admin UI, validation, access | | Local API | Typed, in-process data access (payload.find(...)) — no HTTP, runs server-side | | REST / GraphQL | Auto-generated HTTP APIs over the same collections | | Database adapter | @payloadcms/db-postgres, db-mongodb, or db-sqlite | | Storage adapter | Local disk (dev) or S3/R2/etc. for uploads |
Where it lives in a Next.js app
src/
├── payload.config.ts # the config — collections, globals, db, plugins
├── collections/ # one file per CollectionConfig
│ ├── Users.ts
│ ├── Posts.ts
│ └── Media.ts
├── globals/ # GlobalConfig files
└── app/
├── (payload)/ # Payload's admin + API route group (generated)
└── (frontend)/ # your Next.js front end — uses the Local API
Collections — the core shape
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts', // required, URL-safe identifier
admin: { useAsTitle: 'title', defaultColumns: ['title', 'status'] },
access: { // see access-control reference
read: () => true,
create: ({ req }) => Boolean(req.user),
update: ({ req }) => Boolean(req.user),
delete: ({ req }) => req.user?.role === 'admin',
},
versions: { drafts: true }, // draft/publish + revision history
hooks: { /* lifecycle — see hooks reference */ },
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' },
],
}
| Collection property | Purpose | |---------------------|---------| | slug | Required identifier (and REST/GraphQL route base) | | fields | Required — document shape + UI + validation | | access | Per-operation authorization (read/create/update/delete) | | hooks | Lifecycle entry points (before/after change/read/delete) | | admin | Admin-panel UI (title field, columns, components, groups) | | auth | Turns the collection into an auth collection (e.g. Users) | | upload | Makes it an upload collection (file storage, image sizes) | | versions | Drafts + revision history |
Globals vs Collections
> "If your Collection is only ever meant to contain a single Document, consider using a Global instead."
Globals (GlobalConfig) are singletons — site settings, main nav. Same fields/access/hooks/admin surface, one document.
Fields (the 80/20)
| Type | Use for | |------|---------| | text, textarea, number, email, date, checkbox | Scalars | | richText | Lexical-based rich content | | select, radio | Enumerations | | relationship | Link to other collections (relationTo, hasMany) | | upload | Reference an upload collection (media) | | array | Repeatable sub-field groups | | blocks | Flexible content — choose from defined block types per row | | group | Nested namespaced fields | | row, collapsible, tabs | Admin layout only (no data nesting except tabs with name) | | json, code | Raw structured/code data |
Every field can carry access, hooks, validate, admin.condition (conditional display), and localized: true for i18n. See references/hooks-and-fields.md.
Access control — least privilege by default
Access functions return boolean or a query constraint (row-level filtering). They run for Local API, REST, and GraphQL uniformly.
access: {
// boolean: can this user perform the op at all?
delete: ({ req }) => req.user?.role === 'admin',
// query constraint: WHICH documents can they read? (row-level)
read: ({ req }) => {
if (req.user?.role === 'admin') return true
return { author: { equals: req.user?.id } } // only their own
},
}
- Collection-level (read/create/update/delete) and field-level (
field.access.read/create/update) both exist — use field-level to hide/lock individual fields. - Never bypass access control in custom endpoints. Use
reqcontext; don't hand-roll DB calls that skip it. - The Local API can run with
overrideAccess: truefor trusted server code — use deliberately, not by default.
Full patterns (RBAC, multi-tenant isolation, field-level): references/access-control.md.
Hooks — lifecycle entry points
hooks: {
beforeChange: [({ data, req, operation }) => { /* mutate before save */ return data }],
afterChange: [({ doc, req, operation }) => { /* side effects: revalidate, notify */ return doc }],
beforeRead: [/* ... */],
afterRead: [/* shape outgoing doc */],
beforeDelete: [/* ... */],
afterDelete: [/* cleanup */],
}
Common use: in afterChange, call Next.js revalidatePath() / revalidateTag() to bust the front-end cache on publish. Full hook catalog (collection, field, global, auth hooks): references/hooks-and-fields.md.
Local API (the Next.js superpower)
In server components / route handlers, fetch data in-process — no HTTP round trip, fully typed:
import { getPayload } from 'payload'
import config from '@payload-config'
const payload = await getPayload({ config })
const { docs } = await payload.find({
collection: 'posts',
where: { status: { equals: 'published' } },
depth: 1, // auto-populate relationships one level deep
limit: 10,
})
payload.find / findByID / create / update / delete / findGlobal mirror the REST surface. Access control still applies unless overrideAccess: true.
Caching in Next.js
- Wrap Local API reads in
unstable_cache(orcache) with tags, then invalidate from anafterChangehook viarevalidateTag. depthcontrols relationship population — keep it low to avoid over-fetching.
Decision tables
Database adapter
| Choice | Pick when | |--------|-----------| | Postgres (db-postgres) | Relational data, SQL reporting, Vercel Postgres/Neon/Supabase; migrations matter | | MongoDB (db-mongodb) | Document-shaped data, flexible schema, existing Mongo infra | | SQLite (db-sqlite) | Local/edge, small footprint, simple deploys |
Storage adapter
| Choice | Pick when | |--------|-----------| | Local disk | Dev only — not for serverless (ephemeral FS) | | S3 / R2 (@payloadcms/storage-s3) | Production; put a CDN (CloudFront/Cloudflare) in front; signed URLs for private media; handle 403 on the frontend |
Multi-tenancy
| Approach | Pick when | |----------|-----------| | @payloadcms/plugin-multi-tenant | Standard tenant isolation by a tenant field | | Custom access constraints | Bespoke isolation rules; enforce via row-level read/update constraints |
Common gotchas
| Gotcha | Why | Fix | |--------|-----|-----| | Users see data they shouldn't | read access returns true (no row filter) | Return a query constraint from read, not just true | | Local disk uploads vanish on Vercel | Serverless FS is ephemeral | Use S3/R2 storage adapter | | Stale front-end after publish | Next.js caches the read | revalidateTag/Path in an afterChange hook | | S3 signed URL 403s on frontend | URLs expire | Handle 403 gracefully; refresh URL | | Over-deep relationship fetch | High depth populates everything | Keep depth minimal; populate explicitly | | Custom endpoint leaks data | Bypassed access control | Go through Local API with access on; reserve overrideAccess for trusted paths | | Env not validated | Misconfig fails at runtime | Validate env (zod) at boot | | No real-time collab | Payload has no built-in CRDT | Pair with Liveblocks/Yjs; Payload stays source of truth for final state |
Assets
| File | Use | |------|-----| | assets/collection.config.template.ts | Heavily commented Payload 3 CollectionConfig starter (access + hooks + fields), with adapt-points marked |
See also
typescript-ops— typing config, generated types (payload generate:types)react-ops— custom admin components, server components consuming the Local APIapi-design-ops— REST/GraphQL surface design, pagination, versioningauth-ops— auth collections, sessions/JWT, RBAC/ABAC patterns behind access control
Key external resources
- What is Payload
- Collections · Fields
- Access control
- Hooks
- Local API
- Database · Storage adapters
- Multi-tenant plugin
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: 0xDarkMatter
- Source: 0xDarkMatter/claude-mods
- License: MIT
- Homepage: https://github.com/0xDarkMatter/claude-mods#readme
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.