REST API

基于你的 Collection 和 Global 配置,Payload 会自动生成一个功能完整的 REST API。

REST API 是一个功能完备的 HTTP 客户端,允许你以 RESTful 方式与文档进行交互。它支持所有 CRUD 操作,并具备自动分页、深度查询和排序功能。 所有 Payload API 路由都会挂载并前缀到配置中的 routes.api URL 路径(默认为 /api)。

REST 查询参数:

  • depth - 自动填充关联关系和上传文件
  • locale - 获取特定语言环境的文档
  • fallback-locale - 指定当不存在对应语言环境时的回退语言
  • select - 指定结果中包含哪些字段
  • populate - 指定从填充文档中包含哪些字段
  • limit - 限制返回的文档数量
  • page - 与 limit 配合使用时指定获取哪一页的文档
  • sort - 指定用于排序返回文档的字段
  • where - 指定用于查询文档的高级筛选条件
  • joins - 按字段名称指定每个 join 字段的自定义请求

Collections

每个 collection 都会使用其 slug 值进行挂载。例如,如果一个 collection 的 slug 是 users,那么所有相关路由都会挂载在 /api/users 路径下。

注意:Collection 的 slug 必须使用 kebab-case 格式

所有 CRUD 操作暴露如下:

OperationMethodPathDescription
查询GET/api/{collection-slug}查询分页文档
按 ID 查找GET/api/{collection-slug}/{id}通过 ID 查找特定文档
计数GET/api/{collection-slug}/count统计文档数量
创建POST/api/{collection-slug}创建新文档
更新PATCH/api/{collection-slug}更新匹配 where 查询的所有文档
按 ID 更新PATCH/api/{collection-slug}/{id}通过 ID 更新文档
删除DELETE/api/{collection-slug}删除匹配 where 查询的所有文档
按 ID 删除DELETE/api/{collection-slug}/{id}通过 ID 删除现有文档

认证操作

启用了认证功能的集合还会获得以下端点:

OperationMethodPathDescription
登录POST/api/{user-collection}/login使用邮箱/密码登录用户
登出POST/api/{user-collection}/logout登出用户
解锁POST/api/{user-collection}/unlock解锁用户账户
刷新POST/api/{user-collection}/refresh-token刷新尚未过期的令牌
验证用户POST/api/{user-collection}/verify/{token}用户验证
当前用户GET/api/{user-collection}/me返回当前登录用户及其令牌
忘记密码POST/api/{user-collection}/forgot-password密码重置流程入口
重置密码POST/api/{user-collection}/reset-password重置用户密码

Globals

Globals 不能被创建或删除,因此只开放了两个 REST 端点:

OperationMethodPathDescription
获取 GlobalGET/api/globals/{global-slug}通过 slug 获取 global
更新 GlobalPOST/api/globals/{global-slug}通过 slug 更新 global

偏好设置

除了上述动态生成的端点外,Payload 还提供了 REST 端点来管理管理员用户的偏好设置,这些设置与已认证用户的数据相关。

OperationMethodPathDescription
获取偏好设置GET/api/payload-preferences/{key}通过键获取偏好设置
创建偏好设置POST/api/payload-preferences/{key}通过键创建或更新偏好设置
删除偏好设置DELETE/api/payload-preferences/{key}通过键删除偏好设置

自定义端点

通过在 Payload 配置的不同位置提供 endpoints 数组,你可以为应用程序添加额外的 REST API 端点。自定义端点可用于在现有路由上添加中间件,或为 Payload 应用和插件构建自定义功能。端点可以添加在 Payload 配置的顶层、collectionsglobals 中,并通过你配置的 API 和 slugs 进行访问。

自定义端点默认不进行身份验证。你需要自行确保端点的安全性。

每个端点对象需要包含以下属性:

PropertyDescription
path在 collection 或 globals slug 之后的端点路由字符串
method小写的 HTTP 动词:'get', 'head', 'post', 'put', 'delete', 'connect' 或 'options'
handler接受 req 参数的函数,其中包含 Web Request 属性、当前认证的 user 和 Local API 实例 payload
root当设为 true 时,在 Next.js 应用的根路径定义端点,绕过 Payload 处理程序和 routes.api 子路径。注意:这仅适用于 Payload 配置的顶层端点,在 collectionsglobals 上定义的端点不能设为 root
custom用于添加自定义数据的扩展点(例如用于插件)

示例:

import type { CollectionConfig } from 'payload'

// 一个 'orders' collection,带有额外的跟踪详情路由,可通过 /api/orders/:id/tracking 访问
export const Orders: CollectionConfig = {
  slug: 'orders',
  fields: [
    /* ... */
  ],
  // highlight-start
  endpoints: [
    {
      path: '/:id/tracking',
      method: 'get',
      handler: async (req) => {
        const tracking = await getTrackingInfo(req.routeParams.id)

        if (!tracking) {
          return Response.json({ error: 'not found' }, { status: 404 })
        }

        return Response.json({
          message: `Hello ${req.routeParams.name as string} @ ${req.routeParams.group as string}`,
        })
      },
    },
    {
      path: '/:id/tracking',
      method: 'post',
      handler: async (req) => {
        // `data` 不会自动附加到请求中
        // 如果你想读取请求体
        // 可以使用 `data = await req.json()`
        const data = await req.json()
        await req.payload.update({
          collection: 'tracking',
          data: {
            // 用于更新文档的数据
          },
        })
        return Response.json({
          message: 'successfully updated tracking info',
        })
      },
    },
    {
      path: '/:id/forbidden',
      method: 'post',
      handler: async (req) => {
        // 这是一个需要认证的端点示例
        if (!req.user) {
          return Response.json({ error: 'forbidden' }, { status: 403 })
        }

        // 执行某些操作

        return Response.json({
          message: 'successfully updated tracking info',
        })
      },
    },
  ],
  // highlight-end
}

注意: req 对象包含 payload 属性,可以在端点处理程序中使用,例如调用 req.payload.find(),这些调用将应用 访问控制钩子 功能。

实用技巧

req.data

数据不会自动附加到请求中。你可以通过调用 await req.json() 来读取请求体数据。

或者使用我们的辅助函数,该函数会修改请求对象并附加找到的数据和文件。

import { addDataAndFileToRequest } from 'payload'

// 自定义端点示例
{
  path: '/:id/tracking',
  method: 'post',
  handler: async (req) => {
    await addDataAndFileToRequest(req)
    await req.payload.update({
      collection: 'tracking',
      data: {
        // 用于更新文档的数据
      }
    })
    return Response.json({
      message: 'successfully updated tracking info'
    })
  }
}

req.localereq.fallbackLocale

区域设置和备用区域设置不会自动附加到自定义端点请求中。如果需要添加它们,可以使用这个辅助函数。

import { addLocalesToRequestFromData } from 'payload'

// 自定义端点示例
{
  path: '/:id/tracking',
  method: 'post',
  handler: async (req) => {
    await addLocalesToRequestFromData(req)
    // 现在可以访问 req.locale 和 req.fallbackLocale
    return Response.json({ message: 'success' })
  }
}

headersWithCors

默认情况下,自定义端点不会在响应中处理 CORS 头信息。headersWithCors 函数会检查 Payload 配置,并相应地在响应中设置适当的 CORS 头信息。

import { headersWithCors } from 'payload'

// 自定义端点示例
{
  path: '/:id/tracking',
  method: 'post',
  handler: async (req) => {
    return Response.json(
      { message: 'success' },
      {
        headers: headersWithCors({
          headers: new Headers(),
          req,
        })
      },
    )
  }
}

GET 请求的方法覆盖

Payload 支持方法覆盖功能,允许你使用 HTTP POST 方法发送 GET 请求。这在常规 GET 请求的查询字符串过长时特别有用。

使用方法

要使用此功能,需要在 POST 请求中包含设置为 GETX-Payload-HTTP-Method-Override 请求头。参数应通过请求体发送,并将 Content-Type 设置为 application/x-www-form-urlencoded

示例

以下是使用方法覆盖执行 GET 请求的示例:

使用方法覆盖 (POST)

const res = await fetch(`${api}/${collectionSlug}`, {
  method: 'POST',
  credentials: 'include',
  headers: {
    'Accept-Language': i18n.language,
    'Content-Type': 'application/x-www-form-urlencoded',
    'X-Payload-HTTP-Method-Override': 'GET',
  },
  body: qs.stringify({
    depth: 1,
    locale: 'en',
  }),
})

等效的常规 GET 请求

const res = await fetch(`${api}/${collectionSlug}?depth=1&locale=en`, {
  method: 'GET',
  credentials: 'include',
  headers: {
    'Accept-Language': i18n.language,
  },
})