Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

Select

By default, Payload's APIs will return all fields for a given collection or global. But, you may not need all of that data for all of your queries. Sometimes, you might want just a few fields from the response.

With the Select API, you can define exactly which fields you'd like to retrieve. This can impact the performance of your queries by affecting the load on the database and the size of the response.

Local API

To specify select in the Local API, you can use the select option in your query:

1
import type { Payload } from 'payload'
2
3
// Include mode - result type will only contain: id, text, group.number, and array
4
const getPosts = async (payload: Payload) => {
5
const posts = await payload.find({
6
collection: 'posts',
7
overrideAccess: true,
8
select: {
9
text: true,
10
// select a specific field from group
11
group: {
12
number: true,
13
},
14
// select all fields from array
15
array: true,
16
},
17
})
18
19
return posts
20
}
21
22
// Exclude mode - result type will contain all fields except: array and group.number
23
const getPosts = async (payload: Payload) => {
24
const posts = await payload.find({
25
collection: 'posts',
26
overrideAccess: true,
27
// Select everything except for array and group.number
28
select: {
29
array: false,
30
group: {
31
number: false,
32
},
33
},
34
})
35
36
return posts
37
}

Empty select

The id field is always included in the result, regardless of your select query. If you pass an empty select object, only the id will be returned:

1
const post = await payload.findByID({
2
collection: 'posts',
3
overrideAccess: true,
4
id: '1',
5
select: {},
6
})
7
8
console.log(post) // { id: '1' }

Entity-level select config

Both Collections and Globals accept a top-level select function that runs before every read and lets you transform the caller's select based on request context. It receives { operation, req, select } and returns the final select to apply, replacing the caller's. Return undefined to leave the caller's select untouched.

Common uses:

  • Force a field to always be populated so it's available in hooks / access control.
  • Differentiate API requests from admin panel requests to optimize payload size.
1
import type { CollectionConfig } from 'payload'
2
3
export const Posts: CollectionConfig = {
4
slug: 'posts',
5
// Always include `title`, regardless of the caller's `select`.
6
select: ({ select }) => (select ? { ...select, title: true } : undefined),
7
fields: [
8
// ...
9
],
10
}

The function form replaces the caller's select rather than deep-merging, so spread the caller's select into your returned object when you want to add to it instead of replacing it. Per-document data is not available — this runs before the read.

REST API

To specify select in the REST API, you can use the select parameter in your query:

1
fetch(
2
'https://localhost:3000/api/posts?select[color]=true&select[group][number]=true',
3
)
4
.then((res) => res.json())
5
.then((data) => console.log(data))

To understand the syntax, you need to understand that complex URL search strings are parsed into a JSON object. This one isn't too bad, but more complex queries get unavoidably more difficult to write.

For this reason, we recommend to use the extremely helpful and ubiquitous qs-esm package to parse your JSON / object-formatted queries into query strings:

1
import { stringify } from 'qs-esm'
2
import type { Where } from 'payload'
3
4
const select: Where = {
5
text: true,
6
group: {
7
number: true,
8
},
9
// This query could be much more complex
10
// and QS would handle it beautifully
11
}
12
13
const getPosts = async () => {
14
const stringifiedQuery = stringify(
15
{
16
select, // ensure that `qs` adds the `select` property, too!
17
},
18
{ addQueryPrefix: true },
19
)
20
21
const response = await fetch(
22
`http://localhost:3000/api/posts${stringifiedQuery}`,
23
)
24
// Continue to handle the response below...
25
}

GraphQL API

In the GraphQL API, select is a boolean argument on collection and global queries (including version queries). When set to true, Payload builds a Select projection from your GraphQL selection set and only loads those fields from the database.

Unlike the Local and REST APIs, you do not pass a field map. The fields you request in the GraphQL query become the select projection:

1
query {
2
# highlight-start
3
Posts(select: true) {
4
# highlight-end
5
docs {
6
id
7
text
8
group {
9
number
10
}
11
}
12
}
13
}

The same argument works on singular document queries and globals:

1
query {
2
Post(id: "123", select: true) {
3
id
4
text
5
}
6
}
7
8
query {
9
Header(select: true) {
10
title
11
}
12
}

Without select: true, GraphQL still returns only the fields in your selection set over the wire, but Payload may load the full document from the database. Passing select: true applies the Select API at the database level for better performance.

defaultPopulate collection config property

The defaultPopulate property allows you specify which fields to select when populating the collection from another document. This is especially useful for links where only the slug is needed instead of the entire document.

With this feature, you can dramatically reduce the amount of JSON that is populated from Relationship or Upload fields.

For example, in your content model, you might have a Link field which links out to a different page. When you go to retrieve these links, you really only need the slug of the page.

Loading all of the page content, its related links, and everything else is going to be overkill and will bog down your Payload APIs. Instead, you can define the defaultPopulate property on your Pages collection, so that when Payload "populates" a related Page, it only selects the slug field and therefore returns significantly less JSON:

1
import type { CollectionConfig } from 'payload'
2
3
// The TSlug generic can be passed to have type safety for `defaultPopulate`.
4
// If avoided, the `defaultPopulate` type resolves to `SelectType`.
5
export const Pages: CollectionConfig<'pages'> = {
6
slug: 'pages',
7
// Specify `select`.
8
defaultPopulate: {
9
slug: true,
10
},
11
fields: [
12
{
13
name: 'slug',
14
type: 'text',
15
required: true,
16
},
17
],
18
}

Populate

Setting defaultPopulate will enforce that each time Payload performs a "population" of a related document, only the fields specified will be queried and returned. However, you can override defaultPopulate with the populate property in the Local and REST API:

Local API:

1
import type { Payload } from 'payload'
2
3
const getPosts = async (payload: Payload) => {
4
const posts = await payload.find({
5
collection: 'posts',
6
overrideAccess: true,
7
populate: {
8
// Select only `text` from populated docs in the "pages" collection
9
// Now, no matter what the `defaultPopulate` is set to on the "pages" collection,
10
// it will be overridden, and the `text` field will be returned instead.
11
pages: {
12
text: true,
13
},
14
},
15
})
16
17
return posts
18
}

REST API:

1
fetch('https://localhost:3000/api/posts?populate[pages][text]=true')
2
.then((res) => res.json())
3
.then((data) => console.log(data))

Was this page helpful?

Next

Depth