SQLite

要使用 Payload 与 SQLite 配合,请安装 @payloadcms/db-sqlite 包。该包利用 Drizzle ORM 和 libSQL 与你提供的 SQLite 数据库进行交互。

在开发模式下,它会自动为你管理数据库变更,并暴露完整的迁移控制套件,以便你保持其他数据库环境与你的 schema 同步。DDL 转换会自动生成。

要配置 Payload 使用 SQLite,请按如下方式将 sqliteAdapter 传递给你的 Payload 配置:

import { sqliteAdapter } from '@payloadcms/db-sqlite'

export default buildConfig({
  // 你的配置写在这里
  collections: [
    // 集合写在这里
  ],
  // 在这里配置 SQLite 适配器
  db: sqliteAdapter({
    // SQLite 特定参数写在这里
    // `client.url` 是必填项
    client: {
      url: process.env.DATABASE_URL,
      authToken: process.env.DATABASE_AUTH_TOKEN,
    },
  }),
})

选项

选项描述
client *传递给 @libsql/clientcreateClient客户端连接选项
push在开发模式下禁用 Drizzle 的 db push。默认情况下,push 仅在开发模式下启用。
migrationDir自定义存储迁移文件的目录。
logger传递给 drizzle 的日志记录器实例。默认使用 Payload 的日志记录器。
idType用于指定 ID 列数据类型的字符串,可选 'number' 或 'uuid'。
transactionOptions用于事务的 SQLiteTransactionConfig 对象,或设置为 false 禁用事务。更多详情
localesSuffix附加在存储本地化字段的表名后的字符串。默认为 '_locales'。
relationshipsSuffix附加在存储关系的表名后的字符串。默认为 '_rels'。
versionsSuffix附加在存储版本的表名后的字符串。默认为 '_v'。
beforeSchemaInitDrizzle 模式钩子。在模式构建前运行。更多详情
afterSchemaInitDrizzle 模式钩子。在模式构建后运行。更多详情
generateSchemaOutputFile覆盖 payload generate:db-schema 生成的模式文件路径。默认为 {CWD}/src/payload-generated.schema.ts
autoIncrement传递 true 为 SQLite 主键启用 AUTOINCREMENT,确保已删除行的 ID 不会被重复使用
allowIDOnCreate设置为 true 可以在创建 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-sqlite/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.relations

开发模式下的原型设计

Drizzle 提供了两种在本地开发模式下工作的方式。

第一种是 db push,它会自动将你对 Payload 配置(以及 Drizzle schema)所做的更改推送到数据库,这样你就不必在每次更改 Payload 配置时手动迁移。这仅在开发模式下有效,不应与手动运行 migrate 命令混用。

在开发模式下,如果你所做的更改会导致数据丢失,你会收到警告。默认情况下 push 是启用的,但你可以选择退出。

或者,你可以禁用 push,完全依赖迁移来保持本地数据库与 Payload 配置同步。

迁移工作流

在 SQLite 中,迁移是与 Payload 协作的基本方面,你应该熟悉它们的工作原理。

有关迁移的更多信息,点击此处

Drizzle 模式钩子

beforeSchemaInit

在 schema 构建之前运行。你可以使用这个钩子来扩展数据库结构,添加 Payload 不会管理的表。

import { sqliteAdapter } from '@payloadcms/db-sqlite'
import { integer, sqliteTable } from '@payloadcms/db-sqlite/drizzle/sqlite-core'

sqliteAdapter({
  beforeSchemaInit: [
    ({ schema, adapter }) => {
      return {
        ...schema,
        tables: {
          ...schema.tables,
          addedTable: sqliteTable('added_table', {
            id: integer('id').primaryKey({ autoIncrement: true }),
          }),
        },
      }
    },
  ],
})

一个典型的使用场景是在迁移到 Payload 时保留现有的数据库结构。默认情况下,Payload 会删除当前的数据库 schema,这在某些情况下可能不是我们想要的。 要快速从你的数据库生成 Drizzle schema,你可以使用 Drizzle Introspection 你会得到一个类似这样的 schema.ts 文件:

import {
  sqliteTable,
  text,
  uniqueIndex,
  integer,
} from 'drizzle-orm/sqlite-core'

export const users = sqliteTable('users', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  fullName: text('full_name'),
  phone: text('phone', { length: 256 }),
})

export const countries = sqliteTable(
  'countries',
  {
    id: integer('id').primaryKey({ autoIncrement: true }),
    name: text('name', { length: 256 }),
  },
  (countries) => {
    return {
      nameIndex: uniqueIndex('name_idx').on(countries.name),
    }
  },
)

你可以将它们导入到你的配置中,并通过 beforeSchemaInit 钩子附加到 schema 中,如下所示:

import { sqliteAdapter } from '@payloadcms/db-sqlite'
import { users, countries } from '../drizzle/schema'

sqliteAdapter({
  beforeSchemaInit: [
    ({ schema, adapter }) => {
      return {
        ...schema,
        tables: {
          ...schema.tables,
          users,
          countries,
        },
      }
    },
  ],
})

确保 Payload 的集合不会与表名冲突。例如,如果你已经有一个 slug 为 "users" 的集合,你应该更改这个集合的 slug 或 dbName 来修改表名。

afterSchemaInit

在 Drizzle 模式构建完成后执行。你可以使用这个钩子来修改模式,添加 Payload 不支持的特性,或者添加你不想出现在 Payload 配置中的列。 为了扩展表,Payload 向参数提供了 extendTable 工具函数。你可以参考 Drizzle 文档。 以下示例添加了 extra_integer_column 列以及在 countrycity 列上的复合索引。

import { sqliteAdapter } from '@payloadcms/db-sqlite'
import { index, integer } from '@payloadcms/db-sqlite/drizzle/sqlite-core'
import { buildConfig } from 'payload'

export default buildConfig({
  collections: [
    {
      slug: 'places',
      fields: [
        {
          name: 'country',
          type: 'text',
        },
        {
          name: 'city',
          type: 'text',
        },
      ],
    },
  ],
  db: sqliteAdapter({
    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 { sqliteAdapter } from '@payloadcms/db-sqlite'

sqliteAdapter({
  beforeSchemaInit: [
    ({ schema, adapter }) => {
      // 添加新表
      adapter.rawTables.myTable = {
        name: 'my_table',
        columns: {
          my_id: {
            name: 'my_id',
            type: 'integer',
            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
    },
  ],
})