
如何用 Payload Select 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/payloadPayload 的 REST、Local 和 GraphQL API 默认会返回集合Collection或全局Global文档的所有字段。如果你的页面只需要标题和 slug却把整篇正文、关联文档和上传元数据全部取回来响应体积和数据库开销都会白白放大。Select API 让你精确指定要检索的字段Payload 会在数据库层面实现这个过滤从而同时降低数据库负载和响应大小。适用前提你已经有一个运行中的 Payload 应用至少配置了一个 Collection下文的posts、pages均为文档中的示例集合替换成你自己的 slug 即可。Local API在查询中传入 select 字段映射在 Local API 的查询里加上select选项有两种模式。Include 模式——只返回列出的字段文档示例中的结果只包含id、text、group.number和arrayimport type { Payload } from payload // Include mode - result type will only contain: id, text, group.number, and array const getPosts async (payload: Payload) { const posts await payload.find({ collection: posts, select: { text: true, // select a specific field from group group: { number: true, }, // select all fields from array array: true, }, }) return posts }Exclude 模式——返回除列出字段之外的所有字段文档示例中即返回除array和group.number外的全部字段const getPosts async (payload: Payload) { const posts await payload.find({ collection: posts, // Select everything except for array and group.number select: { array: false, group: { number: false, }, }, }) return posts }嵌套字段group、array 等用对象嵌套表达true表示纳入、false表示排除。空 select 的边界行为id字段无论 select 怎么写都会包含在结果里。传入空对象select: {}时只返回idconst post await payload.findByID({ collection: posts, id: 1, select: {}, }) console.log(post) // { id: 1 }最后这行console.log(post) // { id: 1 }是文档示例输出id: 1是示例值你查询自己的文档时会得到对应的实际 id。仓库中的集成测试 test/select/int.spec.ts 也验证了这一行为select: {}时findByID的返回严格等于{ id }。REST API用 select 查询参数限制 HTTP 请求返回字段REST API 的select参数写在 URL query string 中。REST API 文档列出的查询参数中select的作用就是“specifies which fields to include in the result”fetch( https://localhost:3000/api/posts?select[color]trueselect[group][number]true, ) .then((res) res.json()) .then((data) console.log(data))注意localhost:3000是文档中的默认示例地址替换为你的服务地址。复杂嵌套查询手写 URL 会很快变得难读文档推荐用qs-esm包把对象形式的查询序列化为 query stringimport { stringify } from qs-esm import type { Where } from payload const select: Where { text: true, group: { number: true, }, // This query could be much more complex // and QS would handle it beautifully } const getPosts async () { const stringifiedQuery stringify( { select, // ensure that qs adds the select property, too! }, { addQueryPrefix: true }, ) const response await fetch( http://localhost:3000/api/posts${stringifiedQuery}, ) // Continue to handle the response below... }Globals 使用相同的写法只是端点换成/api/globals。GraphQL APIselect: true 参数GraphQL 里select不是字段映射而是集合与全局查询包括 version 查询上的一个boolean 参数。设为true时Payload 从你的 GraphQL 选择集构建 Select 投影只从数据库加载这些字段query { Posts(select: true) { docs { id text group { number } } } }单文档查询和全局查询同样支持query { Post(id: 123, select: true) { id text } } query { Header(select: true) { title } }这里有一个容易忽略的点不加select: true时GraphQL 在网络层面也只返回你选择的字段但 Payload 仍可能从数据库加载完整文档。传入select: true才能把 Select 下推到数据库层获得文档所说的性能收益。实体级 select 配置给 hooks 和访问控制保底因为 select 在数据库层生效beforeRead和afterReadhooks 可能拿不到完整doc。如果某些字段必须始终出现在 hook / 访问控制逻辑中在 Collection 或 Global 配置里使用实体级select函数import type { CollectionConfig } from payload export const Posts: CollectionConfig { slug: posts, // Always include title, regardless of the callers select. select: ({ select }) (select ? { ...select, title: true } : undefined), fields: [ // ... ], }两个限制需要记住这个函数接收{ operation, req, select }返回的最终select会替换调用方的 select而不是深度合并。想在调用方基础上追加字段时必须像示例那样先展开{ ...select }。它在读取前运行拿不到单条文档的数据。该机制的另一个用途是区分 API 请求和管理面板请求按请求来源优化返回体积。减小关联文档的体积defaultPopulate 与 populate 覆盖响应体积大往往不只来自单文档字段还来自 Relationship 或 Upload 字段 populate 进来的整份关联文档。比如内容模型里一个Link字段指向页面取链接时其实只需要slug。在pages集合上配置defaultPopulate后Payload populate 关联 Page 时只查slugimport type { CollectionConfig } from payload // The TSlug generic can be passed to have type safety for defaultPopulate. // If avoided, the defaultPopulate type resolves to SelectType. export const Pages: CollectionConfigpages { slug: pages, // Specify select. defaultPopulate: { slug: true, }, fields: [ { name: slug, type: text, required: true, }, ], }defaultPopulate对后续每次 populate 强制生效但单次请求仍可用populate覆盖它。Local APIimport type { Payload } from payload const getPosts async (payload: Payload) { const posts await payload.find({ collection: posts, populate: { // Select only text from populated docs in the pages collection // Now, no matter what the defaultPopulate is set to on the pages collection, // it will be overridden, and the text field will be returned instead. pages: { text: true, }, }, }) return posts }REST APIfetch(https://localhost:3000/api/posts?populate[pages][text]true) .then((res) res.json()) .then((data) console.log(data))Upload 集合的注意事项对启用了 Uploads 的集合使用defaultPopulate且要 selecturl字段时必须同时指定filename: true否则 Payload 无法构造正确的文件 URL会返回url: null。验证方式与限制小结验证 select 是否生效最直接的方式是检查返回结构Local API像文档示例那样console.log(post)观察字段是否只剩声明的部分仓库测试 test/select/int.spec.ts 用expect(res).toStrictEqual({ id: postId, number: post.number })这类严格断言验证“select 了什么就只返回什么”。REST API对比带与不带select[...]参数的两次响应 JSON被排除的字段不应出现。GraphQL确认查询加了select: true并且返回 JSON 中不包含选择集之外的字段。使用 select 时的边界条件汇总id永远在结果中无法通过 select 排除。select 在数据库层实现beforeRead/afterRead可能收不到完整doc需要保底字段时用实体级select函数。实体级select函数是替换而非合并追加字段前先展开调用方的select且此时拿不到单文档数据。GraphQL 不加select: true时数据库仍可能加载完整文档只有网络层是精简的。Upload 集合 populate 时需要url就必须同时 selectfilename否则得到url: null。Select 是 Payload 查询性能优化的一项文档建议与其他手段叠加使用给高频查询字段建索引、用depth控制 populate 层级、可预测结果数时用limit和pagination: false详见 查询性能文档和 Performance 总览。【免费下载链接】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),仅供参考