Skip to content

✨ feat(CommerceLayer): expose SDK interceptors via CommerceLayer component and useCommerceLayer hook #748

@acasazza

Description

@acasazza

Problem

The Commerce Layer SDK v7.9.0 already supports request/response interceptors via InterceptorManager (passed in FetchClientOptions), but this library does not expose them.

Users cannot:

  • Intercept and modify outgoing requests (e.g. add custom headers, logging)
  • Intercept responses (e.g. global error handling, retry logic, analytics)
  • Cancel requests programmatically via interceptors

Current SDK Interceptor API

The SDK already exposes the following types:

type InterceptorManager = {
  request?: {
    onSuccess?: (request: RequestObj) => RequestObj | Promise<RequestObj>
    onFailure?: (error: ErrorObj) => ErrorObj | Promise<ErrorObj>
  }
  response?: {
    onSuccess?: (response: Response) => Response | Promise<Response>
    onFailure?: (error: ErrorObj) => ErrorObj | Promise<ErrorObj>
  }
  rawReader?: {
    onSuccess?: (response: Response) => Response | Promise<Response>
    onFailure?: (response: Response) => Response | Promise<Response>
  }
}

Proposed Solution

1. Refactor: move getSdk to packages/core

Currently two separate getSdk implementations exist:

  • packages/react-components/src/utils/getSdk.ts — takes { accessToken, endpoint }, derives slug via getDomain, used in ~30 places
  • packages/core/src/sdk/index.ts — takes { accessToken }, decodes JWT for slug, incomplete (returns void, not exported)

Decision: Unify into packages/core, accepting both endpoint and optional interceptors:

// packages/core/src/sdk/index.ts
export function getSdk({
  accessToken,
  endpoint,
  interceptors,
}: {
  accessToken: string
  endpoint: string
  interceptors?: InterceptorManager
}): CommerceLayerBundle {
  const { slug, domain } = getDomain(endpoint)
  return Sdk({ accessToken, organization: slug, domain, fetch: buildFetch(interceptors) })
}
  • packages/react-components imports getSdk from @commercelayer/core (replacing the local util)
  • packages/core exports getSdk and InterceptorManager type
  • Add interceptors support at the root level — all SDK calls in reducers and utils automatically benefit

2. Refactor: move useCommerceLayer to packages/hooks

Currently useCommerceLayer lives in react-components but all other hooks (useAvailability, usePrices, useSkus, useSkuLists) live in packages/hooks.

Decision: Move useCommerceLayer to packages/hooks to align with the existing hook architecture:

// packages/hooks/src/commerce_layer/useCommerceLayer.ts
export function useCommerceLayer(): ReturnProps {
  const ctx = useContext(CommerceLayerContext) // context stays in react-components or moves to core
  return {
    accessToken: ctx.accessToken,
    sdkClient: (options?: { interceptors?: InterceptorManager }) =>
      getSdk({ accessToken: ctx.accessToken, endpoint: ctx.endpoint, ...options })
  }
}
  • Re-export from packages/react-components for backward compatibility
  • packages/hooks already depends on @commercelayer/core

⚠️ Dependency note: useCommerceLayer currently reads from CommerceLayerContext defined in react-components. The context type (CommerceLayerConfig) should be moved to @commercelayer/core or @commercelayer/hooks to avoid a circular dependency.

3. Expose interceptors prop on <CommerceLayer>

<CommerceLayer accessToken={token} endpoint={endpoint} interceptors={{
  request: {
    onSuccess: (req) => { console.log(req.url); return req },
  },
  response: {
    onFailure: (err) => { reportError(err); return err }
  }
}}>
  ...
</CommerceLayer>

Work breakdown

  • Unify and move getSdk to packages/core, add interceptors param, export types
  • Update all getSdk imports in react-components to use @commercelayer/core
  • Move CommerceLayerConfig context type to @commercelayer/core
  • Move useCommerceLayer to packages/hooks, re-export from react-components for compat
  • Add interceptors prop to <CommerceLayer> component, thread through context
  • Export InterceptorManager type from both @commercelayer/core and @commercelayer/hooks
  • Docs/storybook example added

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions