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

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