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.
|
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 |
|
| Local Option | Description |
|
||||||
| ------------------ | ------------ |
|
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
|
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
|
||||||
| `data` | The data to use within the operation. Required for `create`, `update`. |
|
| `data` | The data to use within the operation. Required for `create`, `update`. |
|
||||||
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
|
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
|
||||||
| `locale` | Specify [locale](/docs/configuration/localization) for any returned documents. |
|
| `locale` | Specify [locale](/docs/configuration/localization) for any returned documents. |
|
||||||
| `fallbackLocale` | Specify a [fallback locale](/docs/configuration/localization) to use 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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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._
|
_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 { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||||
import { findByIDOperation } from '../findByID.js'
|
import { findByIDOperation } from '../findByID.js'
|
||||||
|
|
||||||
export type Options<TSlug extends CollectionSlug> = {
|
export type Options<TSlug extends CollectionSlug = CollectionSlug> = {
|
||||||
collection: TSlug
|
collection: TSlug
|
||||||
/**
|
/**
|
||||||
* context, which will then be passed to req.context, which can be read by hooks
|
* 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
|
user?: Document
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function findByIDLocal<TSlug extends CollectionSlug>(
|
export default async function findByIDLocal<TOptions extends Options>(
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
options: Options<TSlug>,
|
options: TOptions,
|
||||||
): Promise<DataFromCollectionSlug<TSlug>> {
|
): Promise<
|
||||||
|
TOptions['disableErrors'] extends true
|
||||||
|
? DataFromCollectionSlug<TOptions['collection']> | null
|
||||||
|
: DataFromCollectionSlug<TOptions['collection']>
|
||||||
|
> {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
collection: collectionSlug,
|
collection: collectionSlug,
|
||||||
@@ -50,7 +54,7 @@ export default async function findByIDLocal<TSlug extends CollectionSlug>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return findByIDOperation<TSlug>({
|
return findByIDOperation<TOptions['collection']>({
|
||||||
id,
|
id,
|
||||||
collection,
|
collection,
|
||||||
currentDepth,
|
currentDepth,
|
||||||
|
|||||||
@@ -217,11 +217,15 @@ export class BasePayload {
|
|||||||
* @param options
|
* @param options
|
||||||
* @returns document with specified ID
|
* @returns document with specified ID
|
||||||
*/
|
*/
|
||||||
findByID = async <TSlug extends CollectionSlug>(
|
findByID = async <TOptions extends FindByIDOptions>(
|
||||||
options: FindByIDOptions<TSlug>,
|
options: TOptions,
|
||||||
): Promise<DataFromCollectionSlug<TSlug>> => {
|
): Promise<
|
||||||
|
TOptions['disableErrors'] extends true
|
||||||
|
? DataFromCollectionSlug<TOptions['collection']> | null
|
||||||
|
: DataFromCollectionSlug<TOptions['collection']>
|
||||||
|
> => {
|
||||||
const { findByID } = localOperations
|
const { findByID } = localOperations
|
||||||
return findByID<TSlug>(this, options)
|
return findByID<TOptions>(this, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
findGlobal = async <TSlug extends GlobalSlug>(
|
findGlobal = async <TSlug extends GlobalSlug>(
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { Payload } from 'payload'
|
import { randomBytes, randomUUID } from 'crypto'
|
||||||
|
|
||||||
import { randomBytes } from 'crypto'
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { NotFound, type Payload } from 'payload'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
|
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
|
||||||
@@ -1519,6 +1518,17 @@ describe('collections-rest', () => {
|
|||||||
expect(result.errors[0].message).toStrictEqual('Something went wrong.')
|
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>) {
|
async function createPost(overrides?: Partial<Post>) {
|
||||||
|
|||||||
Reference in New Issue
Block a user