4.9 KiB
4.9 KiB
| title | description | tags | ||||||
|---|---|---|---|---|---|---|---|---|
| Database Adapters & Transactions | Database adapters, storage, email, and transaction patterns |
|
Payload CMS Adapters
Database Adapters
MongoDB
import { mongooseAdapter } from '@payloadcms/db-mongodb'
export default buildConfig({
db: mongooseAdapter({
url: process.env.DATABASE_URL,
}),
})
Postgres
import { postgresAdapter } from '@payloadcms/db-postgres'
export default buildConfig({
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URL,
},
push: false, // Don't auto-push schema changes
migrationDir: './migrations',
}),
})
SQLite
import { sqliteAdapter } from '@payloadcms/db-sqlite'
export default buildConfig({
db: sqliteAdapter({
client: {
url: 'file:./payload.db',
},
transactionOptions: {}, // Enable transactions (disabled by default)
}),
})
Transactions
Payload automatically uses transactions for all-or-nothing database operations.
Threading req Through Operations
CRITICAL: When performing nested operations in hooks, always pass req to maintain transaction context.
// ✅ CORRECT: Thread req through nested operations
const resaveChildren: CollectionAfterChangeHook = async ({ collection, doc, req }) => {
// Find children - pass req
const children = await req.payload.find({
collection: 'children',
where: { parent: { equals: doc.id } },
req, // Maintains transaction context
})
// Update each child - pass req
for (const child of children.docs) {
await req.payload.update({
id: child.id,
collection: 'children',
data: { updatedField: 'value' },
req, // Same transaction as parent operation
})
}
}
// ❌ WRONG: Missing req breaks transaction
const brokenHook: CollectionAfterChangeHook = async ({ collection, doc, req }) => {
const children = await req.payload.find({
collection: 'children',
where: { parent: { equals: doc.id } },
// Missing req - separate transaction or no transaction
})
for (const child of children.docs) {
await req.payload.update({
id: child.id,
collection: 'children',
data: { updatedField: 'value' },
// Missing req - if parent operation fails, these updates persist
})
}
}
Why This Matters:
- MongoDB (with replica sets): Creates atomic session across operations
- PostgreSQL: All operations use same Drizzle transaction
- SQLite (with transactions enabled): Ensures rollback on errors
- Without req: Each operation runs independently, breaking atomicity
Manual Transaction Control
const transactionID = await payload.db.beginTransaction()
try {
await payload.create({
collection: 'orders',
data: orderData,
req: { transactionID },
})
await payload.update({
collection: 'inventory',
id: itemId,
data: { stock: newStock },
req: { transactionID },
})
await payload.db.commitTransaction(transactionID)
} catch (error) {
await payload.db.rollbackTransaction(transactionID)
throw error
}
Storage Adapters
Available storage adapters:
- @payloadcms/storage-s3 - AWS S3
- @payloadcms/storage-azure - Azure Blob Storage
- @payloadcms/storage-gcs - Google Cloud Storage
- @payloadcms/storage-r2 - Cloudflare R2
- @payloadcms/storage-vercel-blob - Vercel Blob
- @payloadcms/storage-uploadthing - Uploadthing
AWS S3
import { s3Storage } from '@payloadcms/storage-s3'
export default buildConfig({
plugins: [
s3Storage({
collections: {
media: true,
},
bucket: process.env.S3_BUCKET,
config: {
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
},
region: process.env.S3_REGION,
},
}),
],
})
Email Adapters
Nodemailer (SMTP)
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
export default buildConfig({
email: nodemailerAdapter({
defaultFromAddress: 'noreply@example.com',
defaultFromName: 'My App',
transportOptions: {
host: process.env.SMTP_HOST,
port: 587,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
},
}),
})
Resend
import { resendAdapter } from '@payloadcms/email-resend'
export default buildConfig({
email: resendAdapter({
defaultFromAddress: 'noreply@example.com',
defaultFromName: 'My App',
apiKey: process.env.RESEND_API_KEY,
}),
})
Important Notes
- MongoDB Transactions: Require replica set configuration
- SQLite Transactions: Disabled by default, enable with
transactionOptions: {} - Pass req: Always pass
reqto nested operations in hooks for transaction safety - Point Fields: Not supported in SQLite