diff --git a/.changeset/stable-api-keys.md b/.changeset/stable-api-keys.md new file mode 100644 index 00000000000..1da7ad36ebc --- /dev/null +++ b/.changeset/stable-api-keys.md @@ -0,0 +1,41 @@ +--- +"@clerk/shared": minor +"@clerk/react": minor +"@clerk/clerk-js": minor +"@clerk/ui": minor +--- + +API keys is now generally available. + +### `` component + +```tsx +import { APIKeys } from '@clerk/react'; + +export default function Page() { + return ; +} +``` + +### `useAPIKeys()` hook + +```tsx +import { useAPIKeys } from '@clerk/react'; + +export default function CustomAPIKeys() { + const { data, isLoading, page, pageCount, fetchNext, fetchPrevious } = useAPIKeys({ + pageSize: 10, + initialPage: 1, + }); + + if (isLoading) return
Loading...
; + + return ( + + ); +} +``` diff --git a/packages/clerk-js/src/core/clerk.ts b/packages/clerk-js/src/core/clerk.ts index 1a362b2edac..c845eb2c9eb 100644 --- a/packages/clerk-js/src/core/clerk.ts +++ b/packages/clerk-js/src/core/clerk.ts @@ -1332,15 +1332,11 @@ export class Clerk implements ClerkInterface { }; /** - * @experimental This API is in early access and may change in future releases. - * - * Mount a API keys component at the target element. + * Mount an API keys component at the target element. * @param targetNode Target to mount the APIKeys component. * @param props Configuration parameters. */ public mountAPIKeys = (node: HTMLDivElement, props?: APIKeysProps) => { - logger.warnOnce('Clerk: component is in early access and not yet recommended for production use.'); - if (disabledAllAPIKeysFeatures(this, this.environment)) { if (this.#instanceType === 'development') { throw new ClerkRuntimeError(warnings.cannotRenderAPIKeysComponent, { @@ -1385,9 +1381,7 @@ export class Clerk implements ClerkInterface { }; /** - * @experimental This API is in early access and may change in future releases. - * - * Unmount a API keys component from the target element. + * Unmount an API keys component from the target element. * If there is no component mounted at the target node, results in a noop. * * @param targetNode Target node to unmount the APIKeys component from. diff --git a/packages/clerk-js/src/core/modules/apiKeys/index.ts b/packages/clerk-js/src/core/modules/apiKeys/index.ts index 2c8a05a49aa..77949b70ae0 100644 --- a/packages/clerk-js/src/core/modules/apiKeys/index.ts +++ b/packages/clerk-js/src/core/modules/apiKeys/index.ts @@ -15,10 +15,12 @@ import { convertPageToOffsetSearchParams } from '@/utils/convertPageToOffsetSear import { APIKey, BaseResource } from '../../resources/internal'; export class APIKeys implements APIKeysNamespace { + static readonly #pathRoot = '/api_keys'; + /** * Returns the base options for the FAPI proxy requests. */ - private async getBaseFapiProxyOptions(): Promise { + async #getBaseFapiProxyOptions(): Promise { const token = await BaseResource.clerk.session?.getToken(); if (!token) { throw new ClerkRuntimeError('No valid session token available', { code: 'no_session_token' }); @@ -27,7 +29,7 @@ export class APIKeys implements APIKeysNamespace { return { // Set to an empty string because FAPI Proxy does not include the version in the path. pathPrefix: '', - // Set the session token as a Bearer token in the Authorization header for authentication. + // FAPI Proxy looks for the session token in the Authorization header. headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', @@ -37,11 +39,19 @@ export class APIKeys implements APIKeysNamespace { }; } + /** + * Retrieves a paginated list of API keys. + * + * The subject (owner) is resolved in the following order: + * 1. Explicit `subject` param (user or organization ID) + * 2. Active organization ID + * 3. Current user ID + */ async getAll(params?: GetAPIKeysParams): Promise> { return BaseResource._fetch({ - ...(await this.getBaseFapiProxyOptions()), + ...(await this.#getBaseFapiProxyOptions()), method: 'GET', - path: '/api_keys', + path: APIKeys.#pathRoot, search: convertPageToOffsetSearchParams({ ...params, subject: params?.subject ?? BaseResource.clerk.organization?.id ?? BaseResource.clerk.user?.id ?? '', @@ -59,8 +69,8 @@ export class APIKeys implements APIKeysNamespace { async create(params: CreateAPIKeyParams): Promise { const json = (await BaseResource._fetch({ - ...(await this.getBaseFapiProxyOptions()), - path: '/api_keys', + ...(await this.#getBaseFapiProxyOptions()), + path: APIKeys.#pathRoot, method: 'POST', body: JSON.stringify({ type: 'api_key', @@ -76,9 +86,9 @@ export class APIKeys implements APIKeysNamespace { async revoke(params: RevokeAPIKeyParams): Promise { const json = (await BaseResource._fetch({ - ...(await this.getBaseFapiProxyOptions()), + ...(await this.#getBaseFapiProxyOptions()), method: 'POST', - path: `/api_keys/${params.apiKeyID}/revoke`, + path: `${APIKeys.#pathRoot}/${params.apiKeyID}/revoke`, body: JSON.stringify({ revocation_reason: params.revocationReason, }), diff --git a/packages/react/src/experimental.ts b/packages/react/src/experimental.ts index 348bdfd274a..001206478e7 100644 --- a/packages/react/src/experimental.ts +++ b/packages/react/src/experimental.ts @@ -9,7 +9,6 @@ export type { } from '@clerk/shared/types'; export { - __experimental_useAPIKeys as useAPIKeys, __experimental_PaymentElementProvider as PaymentElementProvider, __experimental_usePaymentElement as usePaymentElement, __experimental_PaymentElement as PaymentElement, diff --git a/packages/react/src/hooks/index.ts b/packages/react/src/hooks/index.ts index ddd62c4fdb4..0db65cab7f7 100644 --- a/packages/react/src/hooks/index.ts +++ b/packages/react/src/hooks/index.ts @@ -10,6 +10,7 @@ export { useUser, useSession, useReverification, + useAPIKeys, __experimental_useCheckout, __experimental_CheckoutProvider, __experimental_usePaymentElement, diff --git a/packages/shared/src/react/hooks/index.ts b/packages/shared/src/react/hooks/index.ts index 9935f6b9440..3bc5d2b1b58 100644 --- a/packages/shared/src/react/hooks/index.ts +++ b/packages/shared/src/react/hooks/index.ts @@ -1,5 +1,5 @@ export { assertContextExists, createContextAndHook } from './createContextAndHook'; -export { useAPIKeys as __experimental_useAPIKeys } from './useAPIKeys'; +export { useAPIKeys } from './useAPIKeys'; export { useOrganization } from './useOrganization'; export { useOrganizationCreationDefaults } from './useOrganizationCreationDefaults'; export type { diff --git a/packages/shared/src/react/hooks/useAPIKeys.tsx b/packages/shared/src/react/hooks/useAPIKeys.tsx index cff886d12c8..69bd41b552b 100644 --- a/packages/shared/src/react/hooks/useAPIKeys.tsx +++ b/packages/shared/src/react/hooks/useAPIKeys.tsx @@ -7,7 +7,7 @@ import { createCacheKeys } from './createCacheKeys'; import { usePagesOrInfinite, useWithSafeValues } from './usePagesOrInfinite'; /** - * @internal + * @interface */ export type UseAPIKeysParams = PaginatedHookConfig< GetAPIKeysParams & { @@ -21,7 +21,7 @@ export type UseAPIKeysParams = PaginatedHookConfig< >; /** - * @internal + * @interface */ export type UseAPIKeysReturn = PaginatedResources< APIKeyResource, @@ -29,8 +29,6 @@ export type UseAPIKeysReturn = PaginatedResources< >; /** - * @internal - * * The `useAPIKeys()` hook provides access to paginated API keys for the current user or organization. * * @example diff --git a/packages/shared/src/types/apiKeys.ts b/packages/shared/src/types/apiKeys.ts index 5dc3c124015..09679504ecf 100644 --- a/packages/shared/src/types/apiKeys.ts +++ b/packages/shared/src/types/apiKeys.ts @@ -23,20 +23,14 @@ export interface APIKeyResource extends ClerkResource { export interface APIKeysNamespace { /** - * @experimental This API is in early access and may change in future releases. - * * Retrieves a paginated list of API keys for the current user or organization. */ getAll(params?: GetAPIKeysParams): Promise>; /** - * @experimental This API is in early access and may change in future releases. - * * Creates a new API key. */ create(params: CreateAPIKeyParams): Promise; /** - * @experimental This API is in early access and may change in future releases. - * * Revokes a given API key by ID. */ revoke(params: RevokeAPIKeyParams): Promise; diff --git a/packages/shared/src/types/clerk.ts b/packages/shared/src/types/clerk.ts index 670a1a21ba0..1a6921a6717 100644 --- a/packages/shared/src/types/clerk.ts +++ b/packages/shared/src/types/clerk.ts @@ -644,11 +644,7 @@ export interface Clerk { unmountPricingTable: (targetNode: HTMLDivElement) => void; /** - * This API is in early access and may change in future releases. - * - * Mount a api keys component at the target element. - * - * @experimental + * Mount an API keys component at the target element. * * @param targetNode - Target to mount the APIKeys component. * @param props - Configuration parameters. @@ -656,14 +652,10 @@ export interface Clerk { mountAPIKeys: (targetNode: HTMLDivElement, props?: APIKeysProps) => void; /** - * This API is in early access and may change in future releases. - * - * Unmount a api keys component from the target element. + * Unmount an API keys component from the target element. * If there is no component mounted at the target node, results in a noop. * - * @experimental - * - * @param targetNode - Target node to unmount the ApiKeys component from. + * @param targetNode - Target node to unmount the APIKeys component from. */ unmountAPIKeys: (targetNode: HTMLDivElement) => void; @@ -1032,9 +1024,6 @@ export interface Clerk { /** * API Keys Object - * - * @experimental - * This API is in early access and may change in future releases. */ apiKeys: APIKeysNamespace; diff --git a/packages/ui/src/components/APIKeys/APIKeys.tsx b/packages/ui/src/components/APIKeys/APIKeys.tsx index f137abff203..352ff251a8c 100644 --- a/packages/ui/src/components/APIKeys/APIKeys.tsx +++ b/packages/ui/src/components/APIKeys/APIKeys.tsx @@ -1,11 +1,6 @@ import { isClerkAPIResponseError } from '@clerk/shared/error'; import { isOrganizationId } from '@clerk/shared/internal/clerk-js/organization'; -import { - __experimental_useAPIKeys as useAPIKeys, - __internal_useOrganizationBase, - useClerk, - useUser, -} from '@clerk/shared/react'; +import { useAPIKeys, __internal_useOrganizationBase, useClerk, useUser } from '@clerk/shared/react'; import type { APIKeyResource } from '@clerk/shared/types'; import { lazy, useState } from 'react';