Add configuration for BMad, Claude, OpenCode, and other AI agent tools and workflows.
6.9 KiB
6.9 KiB
Advanced Features Reference
Jobs Queue
Background task processing:
Define Tasks
// payload.config.ts
export default buildConfig({
jobs: {
tasks: [
{
slug: 'sendEmail',
handler: async ({ payload, job }) => {
const { to, subject, body } = job.input
await sendEmail({ to, subject, body })
},
inputSchema: {
to: { type: 'text', required: true },
subject: { type: 'text', required: true },
body: { type: 'text', required: true },
},
},
{
slug: 'generateThumbnails',
handler: async ({ payload, job }) => {
const { mediaId } = job.input
// Process images...
},
},
],
},
})
Queue Jobs
// In a hook or endpoint
await payload.jobs.queue({
task: 'sendEmail',
input: {
to: 'user@example.com',
subject: 'Welcome!',
body: 'Thanks for signing up.',
},
})
Run Jobs
# In production, run job worker
payload jobs:run
Custom Endpoints
Collection Endpoints
export const Posts: CollectionConfig = {
slug: 'posts',
endpoints: [
{
path: '/publish/:id',
method: 'post',
handler: async (req) => {
const { id } = req.routeParams
const doc = await req.payload.update({
collection: 'posts',
id,
data: {
status: 'published',
publishedAt: new Date(),
},
req,
overrideAccess: false, // Respect permissions
})
return Response.json({ success: true, doc })
},
},
{
path: '/stats',
method: 'get',
handler: async (req) => {
const total = await req.payload.count({ collection: 'posts' })
const published = await req.payload.count({
collection: 'posts',
where: { status: { equals: 'published' } },
})
return Response.json({
total: total.totalDocs,
published: published.totalDocs,
})
},
},
],
}
Global Endpoints
// payload.config.ts
export default buildConfig({
endpoints: [
{
path: '/health',
method: 'get',
handler: async () => {
return Response.json({ status: 'ok' })
},
},
],
})
Plugins
Using Plugins
import { buildConfig } from 'payload'
import { seoPlugin } from '@payloadcms/plugin-seo'
import { formBuilderPlugin } from '@payloadcms/plugin-form-builder'
export default buildConfig({
plugins: [
seoPlugin({
collections: ['posts', 'pages'],
uploadsCollection: 'media',
}),
formBuilderPlugin({
fields: {
text: true,
email: true,
textarea: true,
},
}),
],
})
Creating Plugins
import type { Config, Plugin } from 'payload'
type MyPluginOptions = {
enabled?: boolean
collections?: string[]
}
export const myPlugin = (options: MyPluginOptions): Plugin => {
return (incomingConfig: Config): Config => {
const { enabled = true, collections = [] } = options
if (!enabled) return incomingConfig
return {
...incomingConfig,
collections: (incomingConfig.collections || []).map((collection) => {
if (!collections.includes(collection.slug)) return collection
return {
...collection,
fields: [
...collection.fields,
{
name: 'pluginField',
type: 'text',
admin: { position: 'sidebar' },
},
],
}
}),
}
}
}
Localization
Enable Localization
export default buildConfig({
localization: {
locales: [
{ label: 'English', code: 'en' },
{ label: 'Spanish', code: 'es' },
{ label: 'French', code: 'fr' },
],
defaultLocale: 'en',
fallback: true,
},
})
Localized Fields
{
name: 'title',
type: 'text',
localized: true, // Enable per-locale values
}
Query by Locale
// Local API
const posts = await payload.find({
collection: 'posts',
locale: 'es',
})
// REST API
GET /api/posts?locale=es
// Get all locales
const posts = await payload.find({
collection: 'posts',
locale: 'all',
})
Custom Components
Field Components
// components/CustomTextField.tsx
'use client'
import { useField } from '@payloadcms/ui'
export const CustomTextField: React.FC = () => {
const { value, setValue } = useField()
return (
<input
value={value || ''}
onChange={(e) => setValue(e.target.value)}
/>
)
}
// In field config
{
name: 'customField',
type: 'text',
admin: {
components: {
Field: '/components/CustomTextField',
},
},
}
Custom Views
// Add custom admin page
admin: {
components: {
views: {
Dashboard: '/components/CustomDashboard',
},
},
}
Authentication
Custom Auth Strategies
export const Users: CollectionConfig = {
slug: 'users',
auth: {
strategies: [
{
name: 'api-key',
authenticate: async ({ headers, payload }) => {
const apiKey = headers.get('x-api-key')
if (!apiKey) return { user: null }
const user = await payload.find({
collection: 'users',
where: { apiKey: { equals: apiKey } },
})
return { user: user.docs[0] || null }
},
},
],
},
}
Token Customization
auth: {
tokenExpiration: 7200, // 2 hours
cookies: {
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
domain: process.env.COOKIE_DOMAIN,
},
}
Database Adapters
MongoDB
import { mongooseAdapter } from '@payloadcms/db-mongodb'
db: mongooseAdapter({
url: process.env.DATABASE_URL,
transactionOptions: {
maxCommitTimeMS: 30000,
},
})
PostgreSQL
import { postgresAdapter } from '@payloadcms/db-postgres'
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URL,
},
})
Storage Adapters
S3
import { s3Storage } from '@payloadcms/storage-s3'
plugins: [
s3Storage({
collections: { media: true },
bucket: process.env.S3_BUCKET,
config: {
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY,
},
region: process.env.S3_REGION,
},
}),
]
Vercel Blob
import { vercelBlobStorage } from '@payloadcms/storage-vercel-blob'
plugins: [
vercelBlobStorage({
collections: { media: true },
token: process.env.BLOB_READ_WRITE_TOKEN,
}),
]
Email Adapters
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
email: nodemailerAdapter({
defaultFromAddress: 'noreply@example.com',
defaultFromName: 'My App',
transport: {
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
},
})