Skip to content

Commit 37e1adf

Browse files
authored
fix: findByID adjust type to null if disableErrors: true is passed (#8282)
Fixes #8280 Now, the result type of this operation: ```ts const post = await payload.findByID({ collection: "posts", id, disableErrors: true }) ``` is `Post | null` instead of `Post` when `disableErrors: true` is passed Adds test for the `disableErrors` property and docs.
1 parent 9821aeb commit 37e1adf

File tree

4 files changed

+42
-23
lines changed

4 files changed

+42
-23
lines changed

docs/local-api/overview.mdx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,19 @@ Both options function in exactly the same way outside of one having HMR support
7777

7878
You can specify more options within the Local API vs. REST or GraphQL due to the server-only context that they are executed in.
7979

80-
| Local Option | Description |
81-
| ------------------ | ------------ |
82-
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
83-
| `data` | The data to use within the operation. Required for `create`, `update`. |
84-
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
85-
| `locale` | Specify [locale](/docs/configuration/localization) for any returned documents. |
86-
| `fallbackLocale` | Specify a [fallback locale](/docs/configuration/localization) to use for any returned documents. |
87-
| `overrideAccess` | Skip access control. By default, this property is set to true within all Local API operations. |
88-
| `user` | If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. |
89-
| `showHiddenFields` | Opt-in to receiving hidden fields. By default, they are hidden from returned documents in accordance to your config. |
90-
| `pagination` | Set to false to return all documents and avoid querying for document counts. |
80+
| Local Option | Description |
81+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
82+
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
83+
| `data` | The data to use within the operation. Required for `create`, `update`. |
84+
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
85+
| `locale` | Specify [locale](/docs/configuration/localization) for any returned documents. |
86+
| `fallbackLocale` | Specify a [fallback locale](/docs/configuration/localization) to use for any returned documents. |
87+
| `overrideAccess` | Skip access control. By default, this property is set to true within all Local API operations. |
88+
| `user` | If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. |
89+
| `showHiddenFields` | Opt-in to receiving hidden fields. By default, they are hidden from returned documents in accordance to your config. |
90+
| `pagination` | Set to false to return all documents and avoid querying for document counts. |
9191
| `context` | [Context](/docs/hooks/context), which will then be passed to `context` and `req.context`, which can be read by hooks. Useful if you want to pass additional information to the hooks which shouldn't be necessarily part of the document, for example a `triggerBeforeChange` option which can be read by the BeforeChange hook to determine if it should run or not. |
92+
| `disableErrors` | When set to `true`, errors will not be thrown. Instead, the `findByID` operation will return `null`, and the `find` operation will return an empty documents array. |
9293

9394
_There are more options available on an operation by operation basis outlined below._
9495

packages/payload/src/collections/operations/local/findByID.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { APIError } from '../../../errors/index.js'
66
import { createLocalReq } from '../../../utilities/createLocalReq.js'
77
import { findByIDOperation } from '../findByID.js'
88

9-
export type Options<TSlug extends CollectionSlug> = {
9+
export type Options<TSlug extends CollectionSlug = CollectionSlug> = {
1010
collection: TSlug
1111
/**
1212
* context, which will then be passed to req.context, which can be read by hooks
@@ -26,10 +26,14 @@ export type Options<TSlug extends CollectionSlug> = {
2626
user?: Document
2727
}
2828

29-
export default async function findByIDLocal<TSlug extends CollectionSlug>(
29+
export default async function findByIDLocal<TOptions extends Options>(
3030
payload: Payload,
31-
options: Options<TSlug>,
32-
): Promise<DataFromCollectionSlug<TSlug>> {
31+
options: TOptions,
32+
): Promise<
33+
TOptions['disableErrors'] extends true
34+
? DataFromCollectionSlug<TOptions['collection']> | null
35+
: DataFromCollectionSlug<TOptions['collection']>
36+
> {
3337
const {
3438
id,
3539
collection: collectionSlug,
@@ -50,7 +54,7 @@ export default async function findByIDLocal<TSlug extends CollectionSlug>(
5054
)
5155
}
5256

53-
return findByIDOperation<TSlug>({
57+
return findByIDOperation<TOptions['collection']>({
5458
id,
5559
collection,
5660
currentDepth,

packages/payload/src/index.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,15 @@ export class BasePayload {
217217
* @param options
218218
* @returns document with specified ID
219219
*/
220-
findByID = async <TSlug extends CollectionSlug>(
221-
options: FindByIDOptions<TSlug>,
222-
): Promise<DataFromCollectionSlug<TSlug>> => {
220+
findByID = async <TOptions extends FindByIDOptions>(
221+
options: TOptions,
222+
): Promise<
223+
TOptions['disableErrors'] extends true
224+
? DataFromCollectionSlug<TOptions['collection']> | null
225+
: DataFromCollectionSlug<TOptions['collection']>
226+
> => {
223227
const { findByID } = localOperations
224-
return findByID<TSlug>(this, options)
228+
return findByID<TOptions>(this, options)
225229
}
226230

227231
findGlobal = async <TSlug extends GlobalSlug>(

test/collections-rest/int.spec.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import type { Payload } from 'payload'
2-
3-
import { randomBytes } from 'crypto'
1+
import { randomBytes, randomUUID } from 'crypto'
42
import path from 'path'
3+
import { NotFound, type Payload } from 'payload'
54
import { fileURLToPath } from 'url'
65

76
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
@@ -1519,6 +1518,17 @@ describe('collections-rest', () => {
15191518
expect(result.errors[0].message).toStrictEqual('Something went wrong.')
15201519
})
15211520
})
1521+
1522+
describe('Local', () => {
1523+
it('findByID should throw NotFound if the doc was not found, if disableErrors: true then return null', async () => {
1524+
const post = await createPost()
1525+
const id = typeof post.id === 'string' ? randomUUID() : 999
1526+
await expect(payload.findByID({ collection: 'posts', id })).rejects.toBeInstanceOf(NotFound)
1527+
await expect(
1528+
payload.findByID({ collection: 'posts', id, disableErrors: true }),
1529+
).resolves.toBeNull()
1530+
})
1531+
})
15221532
})
15231533

15241534
async function createPost(overrides?: Partial<Post>) {

0 commit comments

Comments
 (0)