拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Payload SDK 实战指南:以全类型安全方式驱动 Payload REST API

Payload SDK 实战指南以全类型安全方式驱动 Payload REST API【免费下载链接】payloadPayload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for building powerful applications.项目地址: https://gitcode.com/GitHub_Trending/pa/payload本文面向在 Next.js / Payload 全栈项目中希望脱离手写fetch、直接以类型安全方式调用后端接口的开发者。以仓库中 packages/sdk/README.md 为核心骨架结合 packages/sdk/src 源码实现系统讲解payloadcms/sdk的初始化方式、Collections / Globals / 认证 / 版本四类操作、select/populate/joins等类型推导机制以及request、自定义fetch、baseInit等进阶能力。读完本文你将能基于自动生成的Config类型实例化一个接口与 Local API 几乎一致、端到端类型安全的 REST 客户端。一、SDK 是什么一个贴近 Local API 的官方 REST 客户端payloadcms/sdk在仓库中的位置为 packages/sdk/package.json当前版本为4.0.0-canary.14包描述为 The official Payload REST API SDK允许开发者以完全类型安全的方式查询 Payload REST API。它的能力覆盖了全部常规操作包括认证登录、当前用户、刷新令牌、忘记/重置密码、验证邮箱类型安全的select字段筛选与populate关联填充joinsJoin 字段查询简化的文件上传直接传Blob/File或文件 URL草稿、多语言 locale、软删除trash等 Payload 特色查询能力。其方法签名与 Local API非常相似也就是说如果你熟悉在 Payload 后端或 Node 侧直接调用payload.find(...)等 Local API那么使用该 SDK 的前端/外部端调用几乎不需要额外学习成本只是把首参换成描述路由的对象。需要特别说明的是源码注释packages/sdk/src/index.ts中标注了experimental说明该包目前仍处于演进阶段接口可能随版本微调。它依赖仓库根目录下通过payload generate:types生成的类型仓库根 payload-types.ts 即此类文件的实例因此先有类型、后有客户端是使用的前提。二、安装与初始化把生成的 Config 类型作为泛型传入SDK 的类型系统建立在 Payload 自动生成类型之上因此在初始化前你需要先确保项目中有payload-types.ts包含Config、GeneratedTypes等类型定义然后把它作为泛型传入构造函数import { PayloadSDK } from payloadcms/sdk import type { Config } from ./payload-types // Pass your config from generated types as generic const sdk new PayloadSDKConfig({ baseURL: https://example.com/api, })构造函数接收三个字段定义见 packages/sdk/src/index.ts参数必填说明baseURL是REST API 的基础地址例如https://example.com/api。若使用 Payload 的serverURL/routes.api约定此值通常为${serverURL}${routes.api}baseInit否作为RequestInit传入底层fetch的基础配置适合统一设置credentials、公共请求头等。其内容会先于每次调用的init合并fetch否自定义fetch实现默认为globalThis.fetch。典型场景包括注入中间件日志、鉴权、在无 HTTP 服务的测试环境中直接对接 Payload 内部 REST 路由见第六节示例三个参数的合并逻辑可以在构造函数中直接看到packages/sdk/src/index.tsconstructor(args: Args) { this.baseURL args.baseURL this.fetch args.fetch ?? globalThis.fetch.bind(globalThis) this.baseInit args.baseInit ?? {} }三、Collection 常规操作find / findByID / count3.1 find分页查询集合// Find operation const posts await sdk.find({ collection: posts, draft: true, limit: 10, locale: en, page: 1, where: { _status: { equals: published } }, })find返回PaginatedDocsT结构分页文档对象含docs、totalDocs、totalPages、page、hasNextPage等字段。FindOptions的完整参数定义在 packages/sdk/src/collections/find.ts关键参数整理如下参数类型默认说明collectionTSlug—目标 Collection 的 slug由泛型约束为字符串字面量whereWhere—过滤条件完整语法见仓库文档 docs/queries/overview.mdxlimitnumber10每页条数集合配置了defaultLimit时以此为默认pagenumber1页码paginationbooleantrue设为false返回全部文档并跳过 count 统计降低开销也可配合limit只限量不计数sortSort—排序字符串或数组如-createdAtdesc或[group, -createdAt]多字段混合排序draftboolean—是否从版本drafts表中查询草稿depthnumber—控制 relationship/upload 字段的自动填充层级selectTSelect—指定返回哪些字段并联动返回值类型见第五节populatePopulateType—控制被填充关联文档中保留哪些字段joinsJoinQueryfalse控制 Join 字段查询的 count/limit/page/sort/where传false可全部关闭localeall \| TypedLocale—返回指定 locale 的字段值fallbackLocalefalse \| TypedLocale—指定兜底 localetrashbooleanfalse设为true时查询包含被软删除带deletedAt的文档仅当集合启用trash时生效对应的 HTTP 层实现相当直观packages/sdk/src/collections/find.ts把除collection外的选项全部交给buildSearchParams序列化为查询字符串然后对/{collection}发起GETconst response await sdk.request({ args: options, init, method: GET, path: /${options.collection}, }) return response.json()3.2 findByID按 ID 获取单条// Find by ID operation const posts await sdk.findByID({ id, collection: posts, draft: true, locale: en, })相比find它多出一个idnumber | string并支持disableErrors选项——当文档不存在时是否允许静默返回null而非抛错其类型层面通过ApplyDisableErrors...精确反映「可能为 null / 一定存在」两种结果见 packages/sdk/src/index.ts。3.3 count仅统计条数// Count operation const result await sdk.count({ collection: posts, where: { id: { equals: post.id } } })count只返回{ totalDocs: number }packages/sdk/src/collections/count.ts支持where、locale、trash参数内部对/{collection}/count发起GET。适合分页器里计算总页数或展示共 N 条的场景。四、写操作create / update / delete 与文件上传4.1 create创建文档含简化文件上传// Create operation const result await sdk.create({ collection: posts, data: { text: text }, }) // Create operation with a file // file can be either a Blob | File object or a string URL const result await sdk.create({ collection: media, file, data: {} })文件参数支持两种形态这是 SDK 简化文件上传的核心便利类型定义见 packages/sdk/src/collections/create.tsfile?: TSlug extends UploadCollectionSlugT ? Blob | string : never注意该字段的类型是一个条件类型只有目标集合是 Upload 集合时file才被允许且必须为Blob/File或字符串 URL对普通集合传入file会在编译期直接报错。URL 形态会在请求前被解析为File对象实现位于 packages/sdk/src/utilities/resolveFileFromOptions.tsexport const resolveFileFromOptions async (file: Blob | string) { if (typeof file string) { const response await fetch(file) const fileName file.split(/).pop() ?? const blob await response.blob() return new File([blob], fileName, { type: blob.type }) } else { return file } }源码片段同时证实了底层上传协议见 4.4 节携带文件时SDK 不会发送 JSON而是构造multipart/form-data请求体其中文件字段名固定为file业务数据字段名为_payload。4.2 update按 ID 更新 / 批量更新// Update (by ID) operation const result await sdk.update({ collection: posts, id: post.id, data: { text: updated-text, }, }) // Update (bulk) operation const result await sdk.update({ collection: posts, where: { id: { equals: post.id, }, }, data: { text: updated-text-bulk }, })update是一个重载方法packages/sdk/src/index.ts通过选项区分两种形态传入id更新单条内部走PATCH /{collection}/{id}返回单条文档传入where可选limit批量更新内部走PATCH /{collection}返回BulkOperationResult——包含docs成功更新的文档数组与errors{ id, message }[]。两种形态的data类型是DeepPartialRequiredDataFromCollectionSlug...来自ts-essentials的DeepPartial即系统字段id、createdAt、updatedAt、sizes之外的字段可深度部分填写。更新 Upload 集合时同样支持file字段。判断逻辑可见 packages/sdk/src/collections/update.ts无id时返回整个响应 JSON即{ docs, errors }有id时返回json.doc。4.3 delete按 ID 删除 / 批量删除// Delete (by ID) operation const result await sdk.delete({ id: post.id, collection: posts }) // Delete (bulk) operation const result await sdk.delete({ where: { id: { equals: post.id } }, collection: posts })delete同样采用重载packages/sdk/src/index.ts按 ID 删除走DELETE /{collection}/{id}并返回被删文档带where的批量删除走DELETE /{collection}并返回BulkOperationResult。其实现见 packages/sdk/src/collections/delete.ts。若集合启用了trash软删除可通过trash选项配合 REST 语义决定是永久删除还是对回收站文档做操作DeleteBaseOptions.trash注释见 delete.ts。4.4 写操作与文件上传的底层协议request方法是所有操作的最终出口packages/sdk/src/index.ts对json与file的处理逻辑可以概括为只传json设置Content-Type: application/json请求体为JSON.stringify(json)jsonfile同时存在仅 Upload 集合可能出现构造FormData追加file文件与_payloadJSON 字符串两个字段让浏览器自动生成multipart/form-data边界都不存在纯GET/DELETE请求请求体留空。另外所有选项会统一拼到 URL 上${this.baseURL}${path}${buildSearchParams(args)}。序列化逻辑集中在 packages/sdk/src/utilities/buildSearchParams.ts底层使用qs-esm依赖声明见 packages/sdk/package.json要点包括数值型参数depth、page、limit与布尔型draft、trash、pagination会转成字符串只传有值/有类型判断的键未传选项不会污染 URLfallbackLocale序列化为fallback-locale下划线变连字符locale保持原值sort传数组时用逗号拼接为单一sort参数where、select、populate、joins这类结构化对象以嵌套查询语法序列化空参数时返回不追加无意义的?。五、类型安全的核心select / populate / joins 的推导机制SDK 之所以类型安全而不只是带类型关键在类型层把查询选项与返回类型绑定。核心类型定义在 packages/sdk/src/types.ts其中SelectFromCollectionSlugT, TSlug从T[collectionsSelect]中取出该集合的 select 键集合配合payload导出的SelectType约束types.ts#L34-L37TransformCollectionWithSelectT, TSlug, TSelect在传入了合法的select时用TransformDataWithSelect把集合数据修剪成仅含被选中字段的类型未传入select时则原样返回完整文档类型types.ts#L44-L55。于是下面这行代码的返回值会被精确推断为只包含title字段的对象const post await sdk.findByID({ collection: posts, id, select: { title: true }, }) // post 的类型{ id: string; title: string }视选择字段而定相同逻辑也作用于 GlobalsTransformGlobalWithSelect。populate与joins的键同样受集合级类型约束PopulateTypeT取自T[collectionsSelect]的部分映射types.ts#L89而JoinQuerytypes.ts#L78-L87则遍历T[collectionsJoins][TSlug]为每个 join 字段生成{ count, limit, page, sort, where } | false的强类型选项——从类型层面保证你不会写错 join 字段名。在此基础上RequiredDataFromCollectiontypes.ts#L70-L76通过OmitTData, SystemFields把id/createdAt/updatedAt/sizes从创建入参中排除确保 create/update 的data在编译期就被约束为合法字段。六、Globals 操作findGlobal 与 updateGlobalGlobals全局单例内容只有查询与更新两类操作// Find Global operation const result await sdk.findGlobal({ slug: global }) // Update Global operation const result await sdk.updateGlobal({ slug: global, data: { text: some-updated-global } })对应实现分布在 packages/sdk/src/globals 目录findOne.ts、update.ts返回类型同样支持select修剪TransformGlobalWithSelect。对单页网站配置类内容如导航、站点信息、SEO 默认值这类典型 Global 使用场景两个方法即可覆盖读写需求。七、认证操作login / me / refreshToken 等认证方法的collection参数被约束为AuthCollectionSlugT即启用了 auth 的集合完整方法集分布在 packages/sdk/src/auth 目录。7.1 login 与 me// Auth Login operation const result await sdk.login({ collection: users, data: { email: devpayloadcms.com, password: 123456 }, })login走POST /{collection}/login见 login.ts返回{ exp?, message, token?, user }token是后续请求需要的 JWTuser是完整含关系字段类型的用户文档。// Auth Me operation const result await sdk.me( { collection: users }, { headers: { Authorization: JWT ${user.token}, }, }, )me走GET /{collection}/meme.ts用于校验当前会话并拉取当前用户可以看到这里展示了第二个参数为 RequestInit、可传请求头的通用模式详见第九节。返回值中user额外带有_strategysession/jwt 等策略标识。7.2 令牌刷新、忘记/重置密码与邮箱验证// Auth Refresh Token operation const result await sdk.refreshToken( { collection: users }, { headers: { Authorization: JWT ${user.token} } }, ) // Auth Forgot Password operation const result await sdk.forgotPassword({ collection: users, data: { email: user.email }, }) // Auth Reset Password operation const result await sdk.resetPassword({ collection: users, data: { password: 1234567, token: resetPasswordToken }, })refreshToken携带当前 JWT 换取新令牌适合令牌接近过期时无缝续期forgotPassword触发邮件流程仅需 emailresetPassword用邮件中收到的token加新密码完成重置此外verifyEmail见 verifyEmail.ts在集合启用邮箱验证后可调用。八、版本与草稿恢复findVersions / restoreVersion当集合或 Global 开启versions后SDK 提供版本读写接口覆盖列出历史版本、查看某个版本、恢复版本的完整闭环// Find Versions operation const result await sdk.findVersions({ collection: posts, where: { parent: { equals: post.id } }, }) // Find Version by ID operation const result await sdk.findVersionByID({ collection: posts, id: version.id }) // Restore Version operation const result await sdk.restoreVersion({ collection: posts, id, }) // Find Global Versions operation const result await sdk.findGlobalVersions({ slug: global, }) // Find Global Version by ID operation const result await sdk.findGlobalVersionByID({ id: version.id, slug: global }) // Restore Global Version operation const result await sdk.restoreGlobalVersion({ slug: global, id, })对应模块同样按 collections / globals 分组存放于 packages/sdk/src 之下。集合版本方法findVersionByID支持disableErrors返回类型统一包装为TypeWithVersionTrestoreVersion返回恢复后的最新文档restoreGlobalVersion返回TypeWithVersionGlobalData相关签名见 index.ts 与 index.ts。九、进阶定制能力9.1 每个操作都可选的第三参数 RequestInit每个操作find、update、me……都有可选的第三个参数用于向底层RequestInit注入额外配置例如携带认证头。README 对这一点有明确示例await sdk.me( { collection: users, }, { // RequestInit object headers: { Authorization: JWT ${token}, }, }, )在request内部头信息按baseInit.headers与本次init.headers的顺序合并后者覆盖前者其余字段method、body等也遵循baseInit→init的覆盖顺序见 index.ts。9.2 request查询自定义端点request是 SDK 内部所有方法的统一底层同样对用户开放适合调用endpoints扩展的自定义路由await sdk.request({ method: POST, path: /send-data, json: { id: 1, }, })其method被类型约束为DELETE | GET | PATCH | POST | PUTpath为相对于baseURL的路径。响应对象Response会直接返回非 2xx 时抛出的错误已统一封装见第十一节。9.3 自定义 fetch 与 baseInitconst sdk new PayloadSDKConfig({ baseInit: { credentials: include }, baseURL: https://example.com/api, fetch: async (url, init) { console.log(before req) const response await fetch(url, init) console.log(after req) return response }, })这里演示了两个高频用法credentials: include用于携带 Cookie 的会话认证session strategy自定义fetch可在请求前后统一打日志、注入 trace 或做请求重试。SDK 还支持一种更彻底的替换不经过真实 HTTP 服务直接把 SDK 接到 Payload 的 Next.js REST 路由处理器上这在集成测试中非常实用。Args.fetch的 JSDoc 中给出了完整模板见 packages/sdk/src/index.ts核心思路是利用payloadcms/next/routes导出的REST_GET/POST/PATCH/DELETE/PUT(config)在自定义fetch中把 SDK 传入的path search解析成{ slug: [...], params }交给对应 handler。仓库测试目录中test/__helpers/shared/getSDK.ts与 test/sdk 下即有此类直连模式的真实用例可作为落地参考。十、错误处理统一封装的 PayloadSDKErrorSDK 不会把 HTTP 错误静默吞掉。request对!response.ok的分支统一抛出一个PayloadSDKError错误类定义见 packages/sdk/src/errors/PayloadSDKError.ts类型上继承自payload的ErrorResult其属性包括message优先取响应体errors[0].message兜底为response.statusTexterrors响应体中的errors数组字段校验失败时其中包含path、field等细节statusHTTP 状态码response原始Response对象便于进一步排查。响应体解析失败非 JSON时错误信息会回退到状态文本见 index.ts因此捕获时对PayloadSDKError做类型收窄即可获得完整错误上下文try { await sdk.create({ collection: posts, data }) } catch (err) { if (err instanceof PayloadSDKError) { console.error(err.status, err.errors) // 例如 400 字段级校验错误 } }十一、一文速查方法与 HTTP 形态对照结合 packages/sdk/src 各模块实现可整理出如下对照关系路径为相对baseURL推断部分基于集合模块中/${collection}${id ? /id : }的一致模式类别SDK 方法HTTP 形态推断/见实现处查询findGET /{collection}find.ts查询findByIDGET /{collection}/{id}见 index.ts统计countGET /{collection}/countcount.ts创建createPOST /{collection}create.ts更新updatePATCH /{collection}/{id}或PATCH /{collection}update.ts删除deleteDELETE /{collection}/{id}或DELETE /{collection}delete.tsGlobalfindGlobal/updateGlobalglobals 目录认证login/mePOST /{collection}/login、GET /{collection}/me见 login.ts、me.ts认证refreshToken/forgotPassword/resetPassword/verifyEmail对应 Payload 认证 REST 端点模块见 auth 目录版本findVersions/findVersionByID/restoreVersioncollections 下findVersionByID.ts等版本findGlobalVersions/findGlobalVersionByID/restoreGlobalVersionglobals 下版本模块十二、工程实践建议结合 SDK 的设计与 Payload 生态给出几条落地建议用生成的类型驱动客户端项目级Config类型由payload generate:types生成SDK 方法上的集合 slug、字段、locale、join、select 全部受其约束。推荐在共享代码中封装并导出单例// sdk.ts示意 import { PayloadSDK } from payloadcms/sdk import type { GeneratedTypes, SanitizedConfig } from payload import config from payload-config export type TypedPayloadSDK PayloadSDKGeneratedTypes如需测试环境直连不启动服务端可参照 index.ts 中结合payloadcms/next/routes的fetch模板进行注入。认证头统一走第三个参数在服务端使用 JWT 认证时可把Authorization: JWT token放入每次操作的第三个init参数如需全局携带 Cookie 会话优先用baseInit: { credentials: include }。警惕过度请求find默认带 count 统计纯滚动加载可用pagination: false大文档场景用select裁剪返回字段同时获得网络与类型的双重收益。利用BulkOperationResult处理部分失败批量 update/delete 返回{ docs, errors }部分失败并不会导致整体抛错需自行检查errors数组。在 SDK 层统一错误出口捕获PayloadSDKError并依据status分类401 跳登录、400 回显校验错误等避免业务代码逐处try/catch原生fetch。关于depth/select/populate与 Local API 的关系、draft 查询等语义细节可继续阅读仓库中的官方文档docs/queries/depth.mdx、docs/queries/select.mdx、docs/fields/join.mdx、docs/versions/drafts.mdx 与 docs/configuration/localization.mdx这些与 SDK 选项逐一对应能帮助你理解每个参数在服务端的具体含义。【免费下载链接】payloadPayload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for building powerful applications.项目地址: https://gitcode.com/GitHub_Trending/pa/payload创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门