diff --git a/docs/local-api/overview.mdx b/docs/local-api/overview.mdx index 42fe46514..7bbdcc0fb 100644 --- a/docs/local-api/overview.mdx +++ b/docs/local-api/overview.mdx @@ -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._ diff --git a/packages/payload/src/collections/operations/local/findByID.ts b/packages/payload/src/collections/operations/local/findByID.ts index fc9491985..80a03ee01 100644 --- a/packages/payload/src/collections/operations/local/findByID.ts +++ b/packages/payload/src/collections/operations/local/findByID.ts @@ -6,7 +6,7 @@ import { APIError } from '../../../errors/index.js' import { createLocalReq } from '../../../utilities/createLocalReq.js' import { findByIDOperation } from '../findByID.js' -export type Options = { +export type Options = { collection: TSlug /** * context, which will then be passed to req.context, which can be read by hooks @@ -26,10 +26,14 @@ export type Options = { user?: Document } -export default async function findByIDLocal( +export default async function findByIDLocal( payload: Payload, - options: Options, -): Promise> { + options: TOptions, +): Promise< + TOptions['disableErrors'] extends true + ? DataFromCollectionSlug | null + : DataFromCollectionSlug +> { const { id, collection: collectionSlug, @@ -50,7 +54,7 @@ export default async function findByIDLocal( ) } - return findByIDOperation({ + return findByIDOperation({ id, collection, currentDepth, diff --git a/packages/payload/src/index.ts b/packages/payload/src/index.ts index a614dd10f..acfe15f4c 100644 --- a/packages/payload/src/index.ts +++ b/packages/payload/src/index.ts @@ -217,11 +217,15 @@ export class BasePayload { * @param options * @returns document with specified ID */ - findByID = async ( - options: FindByIDOptions, - ): Promise> => { + findByID = async ( + options: TOptions, + ): Promise< + TOptions['disableErrors'] extends true + ? DataFromCollectionSlug | null + : DataFromCollectionSlug + > => { const { findByID } = localOperations - return findByID(this, options) + return findByID(this, options) } findGlobal = async ( diff --git a/test/collections-rest/int.spec.ts b/test/collections-rest/int.spec.ts index b035022c3..14409de72 100644 --- a/test/collections-rest/int.spec.ts +++ b/test/collections-rest/int.spec.ts @@ -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) {