fix: findByID adjust type to null if disableErrors: true is passed (#8282)
Fixes https://github.com/payloadcms/payload/issues/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.
This commit is contained in:
@@ -77,18 +77,19 @@ Both options function in exactly the same way outside of one having HMR support
|
||||
|
||||
You can specify more options within the Local API vs. REST or GraphQL due to the server-only context that they are executed in.
|
||||
|
||||
| Local Option | Description |
|
||||
| ------------------ | ------------ |
|
||||
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
|
||||
| `data` | The data to use within the operation. Required for `create`, `update`. |
|
||||
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
|
||||
| `locale` | Specify [locale](/docs/configuration/localization) for any returned 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. |
|
||||
| `user` | If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. |
|
||||
| `showHiddenFields` | Opt-in to receiving hidden fields. By default, they are hidden from returned documents in accordance to your config. |
|
||||
| `pagination` | Set to false to return all documents and avoid querying for document counts. |
|
||||
| Local Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
|
||||
| `data` | The data to use within the operation. Required for `create`, `update`. |
|
||||
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
|
||||
| `locale` | Specify [locale](/docs/configuration/localization) for any returned 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. |
|
||||
| `user` | If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. |
|
||||
| `showHiddenFields` | Opt-in to receiving hidden fields. By default, they are hidden from returned documents in accordance to your config. |
|
||||
| `pagination` | Set to false to return all documents and avoid querying for document counts. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
|
||||
_There are more options available on an operation by operation basis outlined below._
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { APIError } from '../../../errors/index.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { findByIDOperation } from '../findByID.js'
|
||||
|
||||
export type Options<TSlug extends CollectionSlug> = {
|
||||
export type Options<TSlug extends CollectionSlug = CollectionSlug> = {
|
||||
collection: TSlug
|
||||
/**
|
||||
* 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> = {
|
||||
user?: Document
|
||||
}
|
||||
|
||||
export default async function findByIDLocal<TSlug extends CollectionSlug>(
|
||||
export default async function findByIDLocal<TOptions extends Options>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> {
|
||||
options: TOptions,
|
||||
): Promise<
|
||||
TOptions['disableErrors'] extends true
|
||||
? DataFromCollectionSlug<TOptions['collection']> | null
|
||||
: DataFromCollectionSlug<TOptions['collection']>
|
||||
> {
|
||||
const {
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
@@ -50,7 +54,7 @@ export default async function findByIDLocal<TSlug extends CollectionSlug>(
|
||||
)
|
||||
}
|
||||
|
||||
return findByIDOperation<TSlug>({
|
||||
return findByIDOperation<TOptions['collection']>({
|
||||
id,
|
||||
collection,
|
||||
currentDepth,
|
||||
|
||||
@@ -217,11 +217,15 @@ export class BasePayload {
|
||||
* @param options
|
||||
* @returns document with specified ID
|
||||
*/
|
||||
findByID = async <TSlug extends CollectionSlug>(
|
||||
options: FindByIDOptions<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> => {
|
||||
findByID = async <TOptions extends FindByIDOptions>(
|
||||
options: TOptions,
|
||||
): Promise<
|
||||
TOptions['disableErrors'] extends true
|
||||
? DataFromCollectionSlug<TOptions['collection']> | null
|
||||
: DataFromCollectionSlug<TOptions['collection']>
|
||||
> => {
|
||||
const { findByID } = localOperations
|
||||
return findByID<TSlug>(this, options)
|
||||
return findByID<TOptions>(this, options)
|
||||
}
|
||||
|
||||
findGlobal = async <TSlug extends GlobalSlug>(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
import { randomBytes } from 'crypto'
|
||||
import { randomBytes, randomUUID } from 'crypto'
|
||||
import path from 'path'
|
||||
import { NotFound, type Payload } from 'payload'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
|
||||
@@ -1519,6 +1518,17 @@ describe('collections-rest', () => {
|
||||
expect(result.errors[0].message).toStrictEqual('Something went wrong.')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Local', () => {
|
||||
it('findByID should throw NotFound if the doc was not found, if disableErrors: true then return null', async () => {
|
||||
const post = await createPost()
|
||||
const id = typeof post.id === 'string' ? randomUUID() : 999
|
||||
await expect(payload.findByID({ collection: 'posts', id })).rejects.toBeInstanceOf(NotFound)
|
||||
await expect(
|
||||
payload.findByID({ collection: 'posts', id, disableErrors: true }),
|
||||
).resolves.toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
async function createPost(overrides?: Partial<Post>) {
|
||||
|
||||
Reference in New Issue
Block a user