Skip to content

本地 API

Payload 本地 API 允许您在 Node 服务器上直接执行与 REST 和 GraphQL 相同的操作。在这里,您无需处理服务器延迟或网络速度问题,可以直接与数据库交互。

TIP

本地 API 在 React Server Components 和其他类似的服务器端环境中非常强大。对于其他无头 CMS,您需要通过 HTTP 层向第三方服务器请求数据,这可能会显著增加服务器渲染页面的加载时间。而在 Payload 中,您无需离开服务器即可获取所需数据,速度极快,绝对是一个革命性的改变。

以下是一些使用本地 API 的常见示例:

  • 在 React Server Components 中获取 Payload 数据
  • 通过 Node 种子脚本(seed scripts)批量导入数据
  • 开启自定义 Next.js 路由处理程序,添加额外功能的同时仍依赖 Payload
  • 在访问控制(Access Control)和钩子(Hooks)中使用

访问 Payload

您可以通过两种方式访问当前运行的 Payload 对象:

从参数(args)或请求(req)中访问

在 Payload 内部的大多数地方,例如 Hooks(钩子)、访问控制(Access Control)、验证函数等,您可以直接从函数参数中访问 payload。这通常是最简单的方法。大多数配置函数都会接收 req(请求对象),而 req 绑定了 payload,因此可以通过 req.payload 访问它。

示例:

javascript
const afterChangeHook: CollectionAfterChangeHook = async ({
  req: { payload },
}) => {
  const posts = await payload.find({
    collection: 'posts',
  })
}

导入 Payload

如果您需要在无法通过函数参数或 req 访问 payload 的地方使用它,可以直接导入并初始化 Payload。

javascript
import { getPayload } from 'payload'
import config from '@payload-config'

const payload = await getPayload({ config })

如果您在 Next.js 的开发模式下工作,Payload 将支持热模块替换(HMR)。当您对 Payload 配置进行更改时,Payload 的使用方式将始终与您的更改保持同步。在生产环境中,getPayload 会自动禁用所有 HMR 功能,因此您无需更改代码,我们会在生产模式下为您优化处理。

如果您通过函数参数或 req.payload 访问 Payload,并在 Next.js 中使用它,则 HMR 将自动受到支持。

有关在 Next.js 之外使用 Payload 的更多信息,请点击这里。

本地 API 可用选项

相比于 REST 或 GraphQL,您可以在本地 API 中指定更多选项,因为它们仅在服务器端执行。

选项描述
collection必填,适用于集合操作。指定要操作的集合标识(slug)。
data用于操作的数据。createupdate 操作必填。
depth控制自动填充嵌套关系和上传字段的深度。
locale指定返回文档的语言区域。
select指定返回结果中包含哪些字段。
populate指定从关联文档中填充哪些字段。
fallbackLocale指定返回文档时使用的备用语言区域。
overrideAccess跳过访问控制。默认情况下,此属性在所有本地 API 操作中设为 true
overrideLock默认情况下,文档锁被忽略(true)。设置为 false 时,会强制遵守锁定状态,防止其他用户操作已锁定的文档。
user如果 overrideAccess 设为 false,则可以传递用户对象以用于访问控制检查。
showHiddenFields允许返回隐藏字段。默认情况下,返回的文档会根据配置隐藏这些字段。
pagination设为 false 以返回所有文档,并避免查询文档总数。
context传递 context,它将被传递到 contextreq.context,可供钩子读取。例如,您可以传递 triggerBeforeChange 选项,让 BeforeChange 钩子决定是否运行。
disableErrors设为 true 时,错误不会抛出。findByID 操作返回 nullfind 操作返回空的 documents 数组。
disableTransaction设为 true 时,不会初始化数据库事务。

此外,每个具体操作可能还有其他可用选项,详情请参考相关文档。

事务

当您的数据库使用事务时,您需要将 req 传递给所有本地操作。PostgreSQL 默认使用事务,MongoDB 在使用**副本集(replica set)**时支持事务。

即使不使用事务,传递 req 仍然是推荐的做法。

javascript
const post = await payload.find({
  collection: 'posts',
  req, // passing req is recommended
})

NOTE

默认情况下,本地 API(Local API)会禁用所有访问控制检查,但如果需要,您可以重新启用访问控制,并指定特定的用户来执行操作。

集合

以下集合操作可通过本地 API 进行:

创建

javascript
// The created Post document is returned
const post = await payload.create({
  collection: 'posts', // required
  data: {
    // required
    title: 'sure',
    description: 'maybe',
  },
  locale: 'en',
  fallbackLocale: false,
  user: dummyUserDoc,
  overrideAccess: true,
  showHiddenFields: false,

  // If creating verification-enabled auth doc,
  // you can optionally disable the email that is auto-sent
  disableVerificationEmail: true,

  // If your collection supports uploads, you can upload
  // a file directly through the Local API by providing
  // its full, absolute file path.
  filePath: path.resolve(__dirname, './path-to-image.jpg'),

  // Alternatively, you can directly pass a File,
  // if file is provided, filePath will be omitted
  file: uploadedFile,

  // If you want to create a document that is a duplicate of another document
  duplicateFromID: 'document-id-to-duplicate',
})

查找

javascript
// Result will be a paginated set of Posts.
// See /docs/queries/pagination for more.
const result = await payload.find({
  collection: 'posts', // required
  depth: 2,
  page: 1,
  limit: 10,
  pagination: false, // If you want to disable pagination count, etc.
  where: {}, // pass a `where` query here
  sort: '-title',
  locale: 'en',
  fallbackLocale: false,
  user: dummyUser,
  overrideAccess: false,
  showHiddenFields: true,
})

通过 ID 查找

javascript
// Result will be a Post document.
const result = await payload.findByID({
  collection: 'posts', // required
  id: '507f1f77bcf86cd799439011', // required
  depth: 2,
  locale: 'en',
  fallbackLocale: false,
  user: dummyUser,
  overrideAccess: false,
  showHiddenFields: true,
})

计数

javascript
// Result will be an object with:
// {
//   totalDocs: 10, // count of the documents satisfies query
// }
const result = await payload.count({
  collection: 'posts', // required
  locale: 'en',
  where: {}, // pass a `where` query here
  user: dummyUser,
  overrideAccess: false,
})

通过 ID 更新

javascript
// Result will be the updated Post document.
const result = await payload.update({
  collection: 'posts', // required
  id: '507f1f77bcf86cd799439011', // required
  data: {
    // required
    title: 'sure',
    description: 'maybe',
  },
  depth: 2,
  locale: 'en',
  fallbackLocale: false,
  user: dummyUser,
  overrideAccess: false,
  overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.
  showHiddenFields: true,

  // If your collection supports uploads, you can upload
  // a file directly through the Local API by providing
  // its full, absolute file path.
  filePath: path.resolve(__dirname, './path-to-image.jpg'),

  // If you are uploading a file and would like to replace
  // the existing file instead of generating a new filename,
  // you can set the following property to `true`
  overwriteExistingFiles: true,
})

更新多个

javascript
// Result will be an object with:
// {
//   docs: [], // each document that was updated
//   errors: [], // each error also includes the id of the document
// }
const result = await payload.update({
  collection: 'posts', // required
  where: {
    // required
    fieldName: { equals: 'value' },
  },
  data: {
    // required
    title: 'sure',
    description: 'maybe',
  },
  depth: 0,
  locale: 'en',
  fallbackLocale: false,
  user: dummyUser,
  overrideAccess: false,
  overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.
  showHiddenFields: true,

  // If your collection supports uploads, you can upload
  // a file directly through the Local API by providing
  // its full, absolute file path.
  filePath: path.resolve(__dirname, './path-to-image.jpg'),

  // If you are uploading a file and would like to replace
  // the existing file instead of generating a new filename,
  // you can set the following property to `true`
  overwriteExistingFiles: true,
})

删除

javascript
// Result will be the now-deleted Post document.
const result = await payload.delete({
  collection: 'posts', // required
  id: '507f1f77bcf86cd799439011', // required
  depth: 2,
  locale: 'en',
  fallbackLocale: false,
  user: dummyUser,
  overrideAccess: false,
  overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.
  showHiddenFields: true,
})

删除多个

javascript
// Result will be an object with:
// {
//   docs: [], // each document that is now deleted
//   errors: [], // any errors that occurred, including the id of the errored on document
// }
const result = await payload.delete({
  collection: 'posts', // required
  where: {
    // required
    fieldName: { equals: 'value' },
  },
  depth: 0,
  locale: 'en',
  fallbackLocale: false,
  user: dummyUser,
  overrideAccess: false,
  overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.
  showHiddenFields: true,
})

认证操作

如果一个集合启用了认证,将会提供额外的本地 API 操作:

认证

javascript
// If you're using Next.js, you'll have to import headers from next/headers, like so:
// import { headers as nextHeaders } from 'next/headers'

// you'll also have to await headers inside your function, or component, like so:
// const headers = await nextHeaders()

// If you're using Payload outside of Next.js, you'll have to provide headers accordingly.

// result will be formatted as follows:
// {
//    permissions: { ... }, // object containing current user's permissions
//    user: { ... }, // currently logged in user's document
//    responseHeaders: { ... } // returned headers from the response
// }

const result = await payload.auth({ headers })

登录

javascript
// result will be formatted as follows:
// {
//   token: 'o38jf0q34jfij43f3f...', // JWT used for auth
//   user: { ... } // the user document that just logged in
//   exp: 1609619861 // the UNIX timestamp when the JWT will expire
// }

const result = await payload.login({
  collection: 'users', // required
  data: {
    // required
    email: '[email protected]',
    password: 'rip',
  },
  req: req, // optional, pass a Request object to be provided to all hooks
  depth: 2,
  locale: 'en',
  fallbackLocale: false,
  overrideAccess: false,
  showHiddenFields: true,
})

忘记密码

javascript
// Returned token will allow for a password reset
const token = await payload.forgotPassword({
  collection: 'users', // required
  data: {
    // required
    email: '[email protected]',
  },
  req: req, // pass a Request object to be provided to all hooks
})

重置密码

javascript
// Result will be formatted as follows:
// {
//   token: 'o38jf0q34jfij43f3f...', // JWT used for auth
//   user: { ... } // the user document that just logged in
// }
const result = await payload.resetPassword({
  collection: 'users', // required
  data: {
    // required
    password: req.body.password, // the new password to set
    token: 'afh3o2jf2p3f...', // the token generated from the forgotPassword operation
  },
  req: req, // optional, pass a Request object to be provided to all hooks
})

解锁

javascript
// Returned result will be a boolean representing success or failure
const result = await payload.unlock({
  collection: 'users', // required
  data: {
    // required
    email: '[email protected]',
  },
  req: req, // optional, pass a Request object to be provided to all hooks
  overrideAccess: true,
})

验证

javascript
// Returned result will be a boolean representing success or failure
const result = await payload.verifyEmail({
  collection: 'users', // required
  token: 'afh3o2jf2p3f...', // the token saved on the user as `_verificationToken`
})

全局

以下是通过本地 API 提供的全局操作:

查找

javascript
// Result will be the Header Global.
const result = await payload.findGlobal({
  slug: 'header', // required
  depth: 2,
  locale: 'en',
  fallbackLocale: false,
  user: dummyUser,
  overrideAccess: false,
  showHiddenFields: true,
})

更新

javascript
// Result will be the updated Header Global.
const result = await payload.updateGlobal({
  slug: 'header', // required
  data: {
    // required
    nav: [
      {
        url: 'https://google.com',
      },
      {
        url: 'https://payloadcms.com',
      },
    ],
  },
  depth: 2,
  locale: 'en',
  fallbackLocale: false,
  user: dummyUser,
  overrideAccess: false,
  overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.
  showHiddenFields: true,
})

TypeScript

本地 API 调用将自动推断生成的类型。

以下是使用示例:

javascript
// Properly inferred as `Post` type
const post = await payload.create({
  collection: 'posts',

  // Data will now be typed as Post and give you type hints
  data: {
    title: 'my title',
    description: 'my description',
  },
})