Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add populate property to Local / REST API #8969

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/local-api/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ You can specify more options within the Local API vs. REST or GraphQL due to the
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
| `locale` | Specify [locale](/docs/configuration/localization) for any returned documents. |
| `select` | Specify [select](../queries/select) to control which fields to include to the result. |
| `populate` | Specify [populate](../queries/select#populate) to control which fields to include to the result from populated documents. |
| `fallbackLocale` | Specify a [fallback locale](/docs/configuration/localization) to use for any returned documents. |
| `overrideAccess` | Skip access control. By default, this property is set to true within all Local API operations. |
| `overrideLock` | By default, document locks are ignored (`true`). Set to `false` to enforce locks and prevent operations when a document is locked by another user. [More details](../admin/locked-documents). |
Expand Down
28 changes: 28 additions & 0 deletions docs/queries/select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,31 @@ export const Pages: CollectionConfig<'pages'> = {
],
}
```

## `populate`

You can override `defaultPopulate` with the `populate` property in the Local and REST API

Local API:
```ts
const getPosts = async () => {
const posts = await payload.find({
collection: 'posts',
populate: {
// Select only `text` from populated docs in the "pages" collection
pages: {
text: true,
}, // highlight-line
},
})

return posts
}
```

REST API:
```ts
fetch('https://localhost:3000/api/posts?populate[pages][text]=true') // highlight-line
.then((res) => res.json())
.then((data) => console.log(data))
```
1 change: 1 addition & 0 deletions docs/rest-api/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ All Payload API routes are mounted and prefixed to your config's `routes.api` UR
- [locale](/docs/configuration/localization#retrieving-localized-docs) - retrieves document(s) in a specific locale
- [fallback-locale](/docs/configuration/localization#retrieving-localized-docs) - specifies a fallback locale if no locale value exists
- [select](../queries/select) - specifies which fields to include to the result
- [populate](../queries/select#populate) - specifies which fields to include to the result from populated documents

## Collections

Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/collections/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { isNumber } from 'payload/shared'
import type { CollectionRouteHandler } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const create: CollectionRouteHandler = async ({ collection, req }) => {
Expand All @@ -20,6 +21,7 @@ export const create: CollectionRouteHandler = async ({ collection, req }) => {
data: req.data,
depth: isNumber(depth) ? depth : undefined,
draft,
populate: sanitizePopulate(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
})
Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/routes/rest/collections/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import { isNumber } from 'payload/shared'
import type { CollectionRouteHandler } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const deleteDoc: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, overrideLock, select, where } = req.query as {
const { depth, overrideLock, populate, select, where } = req.query as {
depth?: string
overrideLock?: string
populate?: Record<string, unknown>
select?: Record<string, unknown>
where?: Where
}
Expand All @@ -22,6 +24,7 @@ export const deleteDoc: CollectionRouteHandler = async ({ collection, req }) =>
collection,
depth: isNumber(depth) ? Number(depth) : undefined,
overrideLock: Boolean(overrideLock === 'true'),
populate: sanitizePopulate(populate),
req,
select: sanitizeSelect(select),
where,
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/collections/deleteByID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const deleteByID: CollectionRouteHandlerWithID = async ({
Expand All @@ -28,6 +29,7 @@ export const deleteByID: CollectionRouteHandlerWithID = async ({
collection,
depth: isNumber(depth) ? depth : undefined,
overrideLock: Boolean(overrideLock === 'true'),
populate: sanitizePopulate(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
})
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/collections/duplicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const duplicate: CollectionRouteHandlerWithID = async ({
Expand All @@ -30,6 +31,7 @@ export const duplicate: CollectionRouteHandlerWithID = async ({
collection,
depth: isNumber(depth) ? Number(depth) : undefined,
draft,
populate: sanitizePopulate(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
})
Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/routes/rest/collections/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ import type { CollectionRouteHandler } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeJoinParams } from '../utilities/sanitizeJoinParams.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const find: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, draft, joins, limit, page, select, sort, where } = req.query as {
const { depth, draft, joins, limit, page, populate, select, sort, where } = req.query as {
depth?: string
draft?: string
joins?: JoinQuery
limit?: string
page?: string
populate?: Record<string, unknown>
select?: Record<string, unknown>
sort?: string
where?: Where
Expand All @@ -29,6 +31,7 @@ export const find: CollectionRouteHandler = async ({ collection, req }) => {
joins: sanitizeJoinParams(joins),
limit: isNumber(limit) ? Number(limit) : undefined,
page: isNumber(page) ? Number(page) : undefined,
populate: sanitizePopulate(populate),
req,
select: sanitizeSelect(select),
sort: typeof sort === 'string' ? sort.split(',') : undefined,
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/collections/findByID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizeJoinParams } from '../utilities/sanitizeJoinParams.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const findByID: CollectionRouteHandlerWithID = async ({
Expand All @@ -31,6 +32,7 @@ export const findByID: CollectionRouteHandlerWithID = async ({
depth: isNumber(depth) ? Number(depth) : undefined,
draft: searchParams.get('draft') === 'true',
joins: sanitizeJoinParams(req.query.joins as JoinQuery),
populate: sanitizePopulate(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
})
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/collections/findVersionByID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const findVersionByID: CollectionRouteHandlerWithID = async ({
Expand All @@ -26,6 +27,7 @@ export const findVersionByID: CollectionRouteHandlerWithID = async ({
id,
collection,
depth: isNumber(depth) ? Number(depth) : undefined,
populate: sanitizePopulate(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
})
Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/routes/rest/collections/findVersions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { isNumber } from 'payload/shared'
import type { CollectionRouteHandler } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const findVersions: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, limit, page, select, sort, where } = req.query as {
const { depth, limit, page, populate, select, sort, where } = req.query as {
depth?: string
limit?: string
page?: string
populate?: Record<string, unknown>
select?: Record<string, unknown>
sort?: string
where?: Where
Expand All @@ -24,6 +26,7 @@ export const findVersions: CollectionRouteHandler = async ({ collection, req })
depth: isNumber(depth) ? Number(depth) : undefined,
limit: isNumber(limit) ? Number(limit) : undefined,
page: isNumber(page) ? Number(page) : undefined,
populate: sanitizePopulate(populate),
req,
select: sanitizeSelect(select),
sort: typeof sort === 'string' ? sort.split(',') : undefined,
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/collections/restoreVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'

export const restoreVersion: CollectionRouteHandlerWithID = async ({
id: incomingID,
Expand All @@ -27,6 +28,7 @@ export const restoreVersion: CollectionRouteHandlerWithID = async ({
collection,
depth: isNumber(depth) ? Number(depth) : undefined,
draft: draft === 'true' ? true : undefined,
populate: sanitizePopulate(req.query.populate),
req,
})

Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/routes/rest/collections/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import { isNumber } from 'payload/shared'
import type { CollectionRouteHandler } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const update: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, draft, limit, overrideLock, select, where } = req.query as {
const { depth, draft, limit, overrideLock, populate, select, where } = req.query as {
depth?: string
draft?: string
limit?: string
overrideLock?: string
populate?: Record<string, unknown>
select?: Record<string, unknown>
where?: Where
}
Expand All @@ -27,6 +29,7 @@ export const update: CollectionRouteHandler = async ({ collection, req }) => {
draft: draft === 'true',
limit: isNumber(limit) ? Number(limit) : undefined,
overrideLock: Boolean(overrideLock === 'true'),
populate: sanitizePopulate(populate),
req,
select: sanitizeSelect(select),
where,
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/collections/updateByID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const updateByID: CollectionRouteHandlerWithID = async ({
Expand Down Expand Up @@ -34,6 +35,7 @@ export const updateByID: CollectionRouteHandlerWithID = async ({
depth: isNumber(depth) ? Number(depth) : undefined,
draft,
overrideLock: Boolean(overrideLock === 'true'),
populate: sanitizePopulate(req.query.populate),
publishSpecificLocale,
req,
select: sanitizeSelect(req.query.select),
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/globals/findOne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { isNumber } from 'payload/shared'
import type { GlobalRouteHandler } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const findOne: GlobalRouteHandler = async ({ globalConfig, req }) => {
Expand All @@ -16,6 +17,7 @@ export const findOne: GlobalRouteHandler = async ({ globalConfig, req }) => {
depth: isNumber(depth) ? Number(depth) : undefined,
draft: searchParams.get('draft') === 'true',
globalConfig,
populate: sanitizePopulate(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
})
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/globals/findVersionByID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { isNumber } from 'payload/shared'
import type { GlobalRouteHandlerWithID } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const findVersionByID: GlobalRouteHandlerWithID = async ({ id, globalConfig, req }) => {
Expand All @@ -15,6 +16,7 @@ export const findVersionByID: GlobalRouteHandlerWithID = async ({ id, globalConf
id,
depth: isNumber(depth) ? Number(depth) : undefined,
globalConfig,
populate: sanitizePopulate(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
})
Expand Down
5 changes: 4 additions & 1 deletion packages/next/src/routes/rest/globals/findVersions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { isNumber } from 'payload/shared'
import type { GlobalRouteHandler } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const findVersions: GlobalRouteHandler = async ({ globalConfig, req }) => {
const { depth, limit, page, select, sort, where } = req.query as {
const { depth, limit, page, populate, select, sort, where } = req.query as {
depth?: string
limit?: string
page?: string
populate?: Record<string, unknown>
select?: Record<string, unknown>
sort?: string
where?: Where
Expand All @@ -24,6 +26,7 @@ export const findVersions: GlobalRouteHandler = async ({ globalConfig, req }) =>
globalConfig,
limit: isNumber(limit) ? Number(limit) : undefined,
page: isNumber(page) ? Number(page) : undefined,
populate: sanitizePopulate(populate),
req,
select: sanitizeSelect(select),
sort: typeof sort === 'string' ? sort.split(',') : undefined,
Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/globals/restoreVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { isNumber } from 'payload/shared'
import type { GlobalRouteHandlerWithID } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'

export const restoreVersion: GlobalRouteHandlerWithID = async ({ id, globalConfig, req }) => {
const { searchParams } = req
Expand All @@ -16,6 +17,7 @@ export const restoreVersion: GlobalRouteHandlerWithID = async ({ id, globalConfi
depth: isNumber(depth) ? Number(depth) : undefined,
draft: draft === 'true' ? true : undefined,
globalConfig,
populate: sanitizePopulate(req.query.populate),
req,
})

Expand Down
2 changes: 2 additions & 0 deletions packages/next/src/routes/rest/globals/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { isNumber } from 'payload/shared'
import type { GlobalRouteHandler } from '../types.js'

import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'

export const update: GlobalRouteHandler = async ({ globalConfig, req }) => {
Expand All @@ -21,6 +22,7 @@ export const update: GlobalRouteHandler = async ({ globalConfig, req }) => {
depth: isNumber(depth) ? Number(depth) : undefined,
draft,
globalConfig,
populate: sanitizePopulate(req.query.populate),
publishSpecificLocale,
req,
select: sanitizeSelect(req.query.select),
Expand Down
15 changes: 15 additions & 0 deletions packages/next/src/routes/rest/utilities/sanitizePopulate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { PopulateType } from 'payload'

import { sanitizeSelect } from './sanitizeSelect.js'

export const sanitizePopulate = (unsanitizedPopulate: unknown): PopulateType | undefined => {
if (!unsanitizedPopulate || typeof unsanitizedPopulate !== 'object') {
return
}

for (const k in unsanitizedPopulate) {
unsanitizedPopulate[k] = sanitizeSelect(unsanitizedPopulate[k])
}

return unsanitizedPopulate as PopulateType
}
Loading