obsidian/wiki/payloadcms/queries-select.md
2026-05-15 16:34:07 +01:00

4.8 KiB

title aliases tags sources created updated
Queries — Select & Populate
payload-select
payload-populate
payload-defaultPopulate
payloadcms
queries
performance
select
populate
raw/queries__select.md
2026-05-15 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
// 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.

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.

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:

// 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/<slug>.


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).

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:

// 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


Sources