diff --git a/.changeset/use-api-holder-no-throw.md b/.changeset/use-api-holder-no-throw.md new file mode 100644 index 0000000000..72a2874798 --- /dev/null +++ b/.changeset/use-api-holder-no-throw.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Changed `useApiHolder` to return an empty `ApiHolder` instead of throwing when used outside of an API context. diff --git a/packages/frontend-plugin-api/src/apis/system/useApi.test.tsx b/packages/frontend-plugin-api/src/apis/system/useApi.test.tsx index 4b9d80fb62..7596105810 100644 --- a/packages/frontend-plugin-api/src/apis/system/useApi.test.tsx +++ b/packages/frontend-plugin-api/src/apis/system/useApi.test.tsx @@ -17,7 +17,30 @@ import { renderHook } from '@testing-library/react'; import { createVersionedContextForTesting } from '@backstage/version-bridge'; import { createApiRef } from './ApiRef'; -import { useApi } from './useApi'; +import { useApi, useApiHolder } from './useApi'; + +describe('useApiHolder', () => { + const context = createVersionedContextForTesting('api-context'); + + afterEach(() => { + context.reset(); + }); + + it('should return the API holder from context', () => { + const holder = { get: jest.fn() }; + context.set({ 1: holder }); + + const renderedHook = renderHook(() => useApiHolder()); + expect(renderedHook.result.current).toBe(holder); + }); + + it('should return an empty API holder when there is no context', () => { + const renderedHook = renderHook(() => useApiHolder()); + + const holder = renderedHook.result.current; + expect(holder.get(createApiRef({ id: 'x' }))).toBeUndefined(); + }); +}); describe('useApi', () => { const context = createVersionedContextForTesting('api-context'); diff --git a/packages/frontend-plugin-api/src/apis/system/useApi.tsx b/packages/frontend-plugin-api/src/apis/system/useApi.tsx index 30e39033f7..ffb458d7f3 100644 --- a/packages/frontend-plugin-api/src/apis/system/useApi.tsx +++ b/packages/frontend-plugin-api/src/apis/system/useApi.tsx @@ -19,6 +19,8 @@ import { ApiRef, ApiHolder, TypesToApiRefs } from './types'; import { useVersionedContext } from '@backstage/version-bridge'; import { NotImplementedError } from '@backstage/errors'; +const emptyApiHolder: ApiHolder = { get: () => undefined }; + /** * React hook for retrieving {@link ApiHolder}, an API catalog. * @@ -27,14 +29,10 @@ import { NotImplementedError } from '@backstage/errors'; export function useApiHolder(): ApiHolder { const versionedHolder = useVersionedContext<{ 1: ApiHolder }>('api-context'); if (!versionedHolder) { - throw new NotImplementedError('API context is not available'); + return emptyApiHolder; } - const apiHolder = versionedHolder.atVersion(1); - if (!apiHolder) { - throw new NotImplementedError('ApiContext v1 not available'); - } - return apiHolder; + return versionedHolder.atVersion(1) ?? emptyApiHolder; } /**