Postgres
要使用 Payload 与 Postgres 配合,请安装 @payloadcms/db-postgres
包。它基于 Drizzle ORM 和 node-postgres
来与你提供的 Postgres 数据库进行交互。
另外还提供了 @payloadcms/db-vercel-postgres
包,该包针对 Vercel 使用进行了优化。
在开发模式下,它会自动为你管理数据库变更,并暴露完整的迁移控制套件,让你可以保持其他数据库环境与你的 schema 同步。DDL 转换会自动生成。
要将 Payload 配置为使用 Postgres,请按如下方式将 postgresAdapter
传递给你的 Payload Config:
使用方法
@payloadcms/db-postgres
:
import { postgresAdapter } from '@payloadcms/db-postgres'
export default buildConfig({
// 在此配置 Postgres 适配器
db: postgresAdapter({
// Postgres 特定参数放在这里
// `pool` 是必填项
pool: {
connectionString: process.env.DATABASE_URI,
},
}),
})
@payloadcms/db-vercel-postgres
:
import { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'
export default buildConfig({
// 如果不提供选项,会自动使用 process.env.POSTGRES_URL
db: vercelPostgresAdapter(),
// 可选,可以接受与 @vercel/postgres 包相同的选项
db: vercelPostgresAdapter({
pool: {
connectionString: process.env.DATABASE_URL,
},
}),
})
注意: 如果你使用 vercelPostgresAdapter
并且你的 process.env.POSTGRES_URL
或 pool.connectionString
指向本地数据库(例如主机名包含 localhost
或 127.0.0.1
),我们会使用 pg
模块而不是 @vercel/postgres
进行连接池管理。这是因为 @vercel/postgres
无法与本地数据库配合使用。如果你想禁用此行为,可以向适配器参数传递 forceUseVercelPostgres: true
并按照 Vercel 指南 设置 Docker Neon DB。
选项
选项 | 描述 |
---|---|
pool * | 传递给 Drizzle 和 node-postgres 或 @vercel/postgres 的 Pool 连接选项 |
push | 在开发模式下禁用 Drizzle 的 db push 。默认情况下,push 仅在开发模式下启用。 |
migrationDir | 自定义存储迁移文件的目录。 |
schemaName (实验性) | 指定使用的 postgres schema 名称字符串,默认为 'public'。 |
idType | 用于 id 列数据类型的字符串,可选 'serial' 或 'uuid'。 |
transactionOptions | 用于事务的 PgTransactionConfig 对象,或设置为 false 禁用事务。更多详情 |
disableCreateDatabase | 设置为 true 时,如果数据库不存在则禁用自动创建。默认为 false 。 |
localesSuffix | 附加在本地化字段存储表名后的字符串。默认为 '_locales'。 |
relationshipsSuffix | 附加在关系存储表名后的字符串。默认为 '_rels'。 |
versionsSuffix | 附加在版本存储表名后的字符串。默认为 '_v'。 |
beforeSchemaInit | Drizzle schema 钩子。在 schema 构建前运行。更多详情 |
afterSchemaInit | Drizzle schema 钩子。在 schema 构建后运行。更多详情 |
generateSchemaOutputFile | 覆盖 payload generate:db-schema 生成的 schema 文件路径。默认为 {CWD}/src/payload-generated.schema.ts |
allowIDOnCreate | 设置为 true 时,允许在 create API 操作中使用传入数据中的 id ,而无需使用自定义 ID 字段。 |
访问 Drizzle
Payload 初始化后,此适配器会将 Drizzle 的全部功能暴露给你,以便在需要时使用。
为确保类型安全,你需要首先生成 Drizzle schema:
npx payload generate:db-schema
然后,你可以通过以下方式访问 Drizzle:
import { posts } from './payload-generated-schema'
// 为避免安装 Drizzle,你可以从我们的重新导出路径导入 Drizzle 的所有内容。
import { eq, sql, and } from '@payloadcms/db-postgres/drizzle'
// Drizzle 的查询 API: https://orm.drizzle.team/docs/rqb
const posts = await payload.db.drizzle.query.posts.findMany()
// Drizzle 的选择 API https://orm.drizzle.team/docs/select
const result = await payload.db.drizzle
.select()
.from(posts)
.where(
and(eq(posts.id, 50), sql`lower(${posts.title}) = 'example post title'`),
)
表、关系和枚举
除了直接暴露 Drizzle 外,所有的表、Drizzle 关系和枚举配置也通过 payload.db
属性暴露给你。
- 表 -
payload.db.tables
- 枚举 -
payload.db.enums
- 关系 -
payload.db.relations
开发模式下的原型设计
Drizzle 提供了两种在本地开发模式下工作的方式。
第一种是 db push
,它会自动将你对 Payload Config(以及 Drizzle schema)所做的更改推送到数据库,这样你就不必每次更改 Payload Config 时都手动迁移。这仅在开发模式下有效,不应与手动运行 migrate
命令混用。
在开发模式下,如果你所做的更改会导致数据丢失,你会收到警告。默认情况下 push 是启用的,但你可以选择退出。
或者,你可以禁用 push
,完全依赖迁移来保持本地数据库与 Payload Config 同步。
迁移工作流
在 Postgres 中,迁移是使用 Payload 的基础工作,你应该熟悉它们的工作原理。
有关迁移的更多信息,点击此处。
Drizzle 模式钩子
beforeSchemaInit
在 schema 构建之前运行。你可以使用这个钩子来扩展数据库结构,添加 Payload 不会管理的表。
import { postgresAdapter } from '@payloadcms/db-postgres'
import {
integer,
pgTable,
serial,
} from '@payloadcms/db-postgres/drizzle/pg-core'
postgresAdapter({
beforeSchemaInit: [
({ schema, adapter }) => {
return {
...schema,
tables: {
...schema.tables,
addedTable: pgTable('added_table', {
id: serial('id').notNull(),
}),
},
}
},
],
})
一个典型的使用场景是在迁移到 Payload 时保留现有的数据库结构。默认情况下,Payload 会删除当前的数据库 schema,这在某些情况下可能不是我们想要的。
要快速从数据库生成 Drizzle schema,你可以使用 Drizzle Introspection。
你会得到一个类似这样的 schema.ts
文件:
import {
pgTable,
uniqueIndex,
serial,
varchar,
text,
} from 'drizzle-orm/pg-core'
export const users = pgTable('users', {
id: serial('id').primaryKey(),
fullName: text('full_name'),
phone: varchar('phone', { length: 256 }),
})
export const countries = pgTable(
'countries',
{
id: serial('id').primaryKey(),
name: varchar('name', { length: 256 }),
},
(countries) => {
return {
nameIndex: uniqueIndex('name_idx').on(countries.name),
}
},
)
你可以将这些导入到配置中,并通过 beforeSchemaInit
钩子将它们附加到 schema 中,如下所示:
import { postgresAdapter } from '@payloadcms/db-postgres'
import { users, countries } from '../drizzle/schema'
postgresAdapter({
beforeSchemaInit: [
({ schema, adapter }) => {
return {
...schema,
tables: {
...schema.tables,
users,
countries,
},
}
},
],
})
确保 Payload 的集合不会与表名冲突。例如,如果你已经有一个 slug 为 "users" 的集合,你应该更改这个集合的 slug 或 dbName
来修改对应的表名。
afterSchemaInit
在 Drizzle schema 构建完成后执行。你可以使用这个钩子来修改 schema,添加 Payload 不支持的特性,或者添加不想出现在 Payload 配置中的列。
要扩展表,Payload 向参数提供了 extendTable
工具函数。你可以参考 Drizzle 文档。
以下示例添加了 extra_integer_column
列,并在 country
和 city
列上创建了复合索引。
import { postgresAdapter } from '@payloadcms/db-postgres'
import { index, integer } from '@payloadcms/db-postgres/drizzle/pg-core'
import { buildConfig } from 'payload'
export default buildConfig({
collections: [
{
slug: 'places',
fields: [
{
name: 'country',
type: 'text',
},
{
name: 'city',
type: 'text',
},
],
},
],
db: postgresAdapter({
afterSchemaInit: [
({ schema, extendTable, adapter }) => {
extendTable({
table: schema.tables.places,
columns: {
extraIntegerColumn: integer('extra_integer_column'),
},
extraConfig: (table) => ({
country_city_composite_index: index(
'country_city_composite_index',
).on(table.country, table.city),
}),
})
return schema
},
],
}),
})
关于生成 schema 的说明:
通过 schema hooks 添加的列和表不会被包含在通过 payload generate:db-schema
生成的 Drizzle schema 中。
如果你希望它们出现在生成的 schema 中,你需要手动编辑该文件,或者在 beforeSchemaInit
中修改 Payload 内部的"原始"SQL schema:
import { postgresAdapter } from '@payloadcms/db-postgres'
postgresAdapter({
beforeSchemaInit: [
({ schema, adapter }) => {
// 添加新表
adapter.rawTables.myTable = {
name: 'my_table',
columns: {
my_id: {
name: 'my_id',
type: 'serial',
primaryKey: true,
},
},
}
// 向 Payload 生成的表中添加新列:
adapter.rawTables.posts.columns.customColumn = {
name: 'custom_column',
// 注意 Payload SQL 并不支持 Drizzle 的所有功能
type: 'integer',
notNull: true,
}
// 向 Payload 生成的表中添加新索引:
adapter.rawTables.posts.indexes.customColumnIdx = {
name: 'custom_column_idx',
unique: true,
on: ['custom_column'],
}
return schema
},
],
})