From 2d1999e954ddd28f622e46a9989be0e78cf5735f Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Fri, 15 May 2026 16:34:07 +0100 Subject: [PATCH] vault backup: 2026-05-15 16:34:07 --- raw/{ => _processed}/queries__select.md | 0 wiki/_master-index.md | 2 +- wiki/payloadcms/_index.md | 1 + wiki/payloadcms/queries-select.md | 161 ++++++++++++++++++++++++ 4 files changed, 163 insertions(+), 1 deletion(-) rename raw/{ => _processed}/queries__select.md (100%) create mode 100644 wiki/payloadcms/queries-select.md diff --git a/raw/queries__select.md b/raw/_processed/queries__select.md similarity index 100% rename from raw/queries__select.md rename to raw/_processed/queries__select.md diff --git a/wiki/_master-index.md b/wiki/_master-index.md index bfa52a4..92369eb 100644 --- a/wiki/_master-index.md +++ b/wiki/_master-index.md @@ -35,7 +35,7 @@ This 3-hop pattern works for hundreds of articles without vector search. | [[wiki/reports/_index\|reports/]] | Weekly and monthly summaries — generate: `uv run python scripts/report-generator.py --weekly` | 1 | | [[wiki/infrastructure/_index\|infrastructure/]] | Server inventory: all 10 SSH hosts — optical, optical-dev, optical-prod, baic, librechat, modocmms, box-cli, aimpress, pve | 12 | | [[wiki/testing/_index\|testing/]] | Web app testing: functional, performance, security, UI types; TDD/BDD/Agile methodologies; Selenium/Cypress/Playwright/JMeter/OWASP ZAP tools | 1 | -| [[wiki/payloadcms/_index\|payloadcms/]] | Full Payload CMS reference — getting started, config, database (Postgres/MongoDB/SQLite), all 22 field types, access control, hooks, authentication (cookies, JWT, API keys, custom strategies, token data), admin UI, custom components, Lexical rich text, live preview, versions/drafts, Local/REST/GraphQL APIs, queries, plugins, jobs queue, upload, ecommerce, production deploy, TypeScript, migration guides, i18n, localization, hierarchy | 127 | +| [[wiki/payloadcms/_index\|payloadcms/]] | Full Payload CMS reference — getting started, config, database (Postgres/MongoDB/SQLite), all 22 field types, access control, hooks, authentication (cookies, JWT, API keys, custom strategies, token data), admin UI, custom components, Lexical rich text, live preview, versions/drafts, Local/REST/GraphQL APIs, queries, plugins, jobs queue, upload, ecommerce, production deploy, TypeScript, migration guides, i18n, localization, hierarchy | 128 | | [[wiki/shared-patterns/_index\|shared-patterns/]] | Oliver Agency standard library patterns: httpx, structlog, pydantic-settings, alembic — reuse before writing from scratch | 4 | | [[wiki/mistakes/_index\|mistakes/]] | Anti-patterns extracted from sessions — per-stack running lists (fastapi, react, docker, postgres, general) — injected at session start | 5 | diff --git a/wiki/payloadcms/_index.md b/wiki/payloadcms/_index.md index a1f12b8..fb51c43 100644 --- a/wiki/payloadcms/_index.md +++ b/wiki/payloadcms/_index.md @@ -128,3 +128,4 @@ | [[wiki/payloadcms/production-preventing-abuse\|Production — Preventing API Abuse]] | Login rate limiting (maxLoginAttempts/lockTime), maxDepth, CSRF, CORS, GraphQL complexity limits, malicious file upload mitigation | raw/production__preventing-abuse.md | 2026-05-15 | | [[wiki/payloadcms/queries-depth\|Queries — Depth (Relationship Population)]] | depth=0 (IDs only) vs depth=N (full objects), defaultDepth config, field-level maxDepth cap, performance notes, GraphQL exception | raw/queries__depth.md | 2026-05-15 | | [[wiki/payloadcms/queries-pagination\|Queries — Pagination]] | Default pagination (limit/page/pagination options), response shape (docs + metadata), limit:1 perf tip, disabling pagination for bulk ops | raw/queries__pagination.md | 2026-05-15 | +| [[wiki/payloadcms/queries-select\|Queries — Select & Populate]] | Include/exclude field selection at DB level, entity-level `select` function for hooks/access, `defaultPopulate` for relationship optimization, `populate` override | raw/queries__select.md | 2026-05-15 | diff --git a/wiki/payloadcms/queries-select.md b/wiki/payloadcms/queries-select.md new file mode 100644 index 0000000..5632c00 --- /dev/null +++ b/wiki/payloadcms/queries-select.md @@ -0,0 +1,161 @@ +--- +title: "Queries — Select & Populate" +aliases: [payload-select, payload-populate, payload-defaultPopulate] +tags: [payloadcms, queries, performance, select, populate] +sources: [raw/queries__select.md] +created: 2026-05-15 +updated: 2026-05-15 +--- + +## Overview + +By default Payload returns **all fields** for every query. The Select API lets you specify exactly which fields to return, reducing DB load and response size. + +--- + +## Include / Exclude Modes + +`select` operates in one of two modes depending on the values you pass: + +| Mode | How | Result | +|------|-----|--------| +| **Include** | Set fields to `true` | Only those fields + `id` | +| **Exclude** | Set fields to `false` | All fields except those | + +```ts +// Include mode — only id, text, group.number, array +await payload.find({ + collection: 'posts', + select: { + text: true, + group: { number: true }, + array: true, + }, +}) + +// Exclude mode — all fields except array and group.number +await payload.find({ + collection: 'posts', + select: { + array: false, + group: { number: false }, + }, +}) +``` + +### Empty select + +Always returns **only `id`** — the `id` field is always included regardless of your `select`. + +```ts +const post = await payload.findByID({ collection: 'posts', id: '1', select: {} }) +// → { id: '1' } +``` + +--- + +## Entity-Level `select` Config + +Both Collections and Globals accept a top-level `select` function that runs **before every read**. Use it to force fields into every query regardless of what the caller requests. + +```ts +export const Posts: CollectionConfig = { + slug: 'posts', + // Always include `title` — required by access control / hooks + select: ({ select }) => (select ? { ...select, title: true } : undefined), + fields: [ /* ... */ ], +} +``` + +- Receives `{ operation, req, select }` → returns the final `select` to apply +- **Replaces** (not merges) the caller's select — spread `select` when you want to add +- Return `undefined` to leave the caller's select untouched +- Per-document data is **not** available (runs before the read) + +> **Warning:** `beforeRead` and `afterRead` hooks may not receive the full `doc` when `select` is active. Use entity-level config to guarantee field availability in hooks/access control. + +--- + +## REST API + +Use bracket notation in query params; prefer `qs-esm` for complex queries: + +```ts +// Direct +fetch('https://localhost:3000/api/posts?select[color]=true&select[group][number]=true') + +// With qs-esm (recommended) +import { stringify } from 'qs-esm' + +const stringifiedQuery = stringify({ select: { text: true, group: { number: true } } }, { addQueryPrefix: true }) +const response = await fetch(`http://localhost:3000/api/posts${stringifiedQuery}`) +``` + +Same syntax works for Globals via `/api/globals/`. + +--- + +## `defaultPopulate` — Optimize Relationship Population + +Set on a Collection to control which fields are returned when that collection is **populated** (via Relationship or Upload fields in other documents). + +```ts +export const Pages: CollectionConfig<'pages'> = { + slug: 'pages', + defaultPopulate: { + slug: true, // only return slug when populated from another doc + }, + fields: [{ name: 'slug', type: 'text', required: true }], +} +``` + +> **Warning (Upload fields):** If you include `url` in `defaultPopulate`, you **must** also include `filename: true`, otherwise Payload cannot construct the file URL and returns `url: null`. + +--- + +## `populate` Override + +Override `defaultPopulate` per-query via the `populate` option: + +```ts +// Local API +await payload.find({ + collection: 'posts', + populate: { + pages: { text: true }, // overrides defaultPopulate on Pages collection + }, +}) + +// REST API +fetch('https://localhost:3000/api/posts?populate[pages][text]=true') +``` + +--- + +## Key Takeaways + +- `select` is applied at the **database level** — efficient but means hooks may receive partial docs +- Use **entity-level `select` function** to guarantee fields in hooks/access control +- `id` is always returned — even with `select: {}` +- `defaultPopulate` on a Collection drastically reduces JSON from relationship fields — set it for any collection only needed as a reference (e.g. pages linked by slug) +- For Upload collections: `defaultPopulate` with `url` requires `filename: true` alongside it +- Use `qs-esm` for REST API select/populate — raw bracket notation gets unwieldy fast + +--- + +## Related + +- [[wiki/payloadcms/queries|Queries — Full Reference]] +- [[wiki/payloadcms/queries-depth|Queries — Depth (Relationship Population)]] +- [[wiki/payloadcms/queries-pagination|Queries — Pagination]] +- [[wiki/payloadcms/fields-relationship|Relationship Field]] +- [[wiki/payloadcms/fields-upload|Upload Field]] +- [[wiki/payloadcms/hooks|Hooks Overview]] +- [[wiki/payloadcms/local-api|Local API]] +- [[wiki/payloadcms/rest-api|REST API]] + +--- + +## Sources + +- `raw/queries__select.md` — https://payloadcms.com/docs/queries/select