Skip to content

Commit

Permalink
feat: add populate property to Local / REST API
Browse files Browse the repository at this point in the history
  • Loading branch information
r1tsuu committed Nov 2, 2024
1 parent 59ff8c1 commit d490a8f
Show file tree
Hide file tree
Showing 67 changed files with 489 additions and 46 deletions.
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

0 comments on commit d490a8f

Please sign in to comment.