@techsio/storefront-data
decide-app-specific-overrides-vs-shared-platform
compositionLoad this skill when deciding whether storefront-specific wrappers, DTOs, address adapters, or backend behavior should stay in the app or move into @techsio/storefront-data. Use it for thin wrapper rules, customer-specific read models, and deciding when repeated backend logic becomes shared platform behavior.
Decide app-specific overrides vs shared platform
Setup
Default to the shared preset surface. Add a local wrapper only when the app contributes real policy or UX behavior.
// src/lib/auth-hooks.ts
import { storefront } from "@/src/lib/storefront"
import { toast } from "sonner"
export function useLoginWithToast() {
return storefront.hooks.auth.useLogin({
onSuccess: () => {
toast.success("Logged in")
},
})
}Core Patterns
Keep customer-specific DTOs and read models in the app
// apps/n1/src/lib/customer-models.ts
export type N1ProfileCard = {
email: string
loyaltyTier: string
}The shared package should own reusable backend-facing contracts. A storefront-local view model stays local until another storefront needs the same shape.
Keep address customization behind the shared contract
// apps/n1/src/lib/address-adapter.ts
import type { CheckoutCartAddressAdapter } from "@techsio/storefront-data/shared/address"
export const checkoutCartAddressAdapter: CheckoutCartAddressAdapter<FormValues> = {
toCreateInput(values) {
return {
first_name: values.firstName,
last_name: values.lastName,
address_1: values.street,
city: values.city,
country_code: values.countryCode,
postal_code: values.postalCode,
}
},
toPatchInput(values) {
return {
first_name: values.firstName,
last_name: values.lastName,
address_1: values.street,
city: values.city,
country_code: values.countryCode,
postal_code: values.postalCode,
}
},
}Promote repeated backend behavior into the shared package
// apps/n1/src/lib/checkout.ts
export function useCheckoutWithSharedPaymentRule(cartId: string, regionId: string) {
return storefront.flows.checkout.useCompleteCheckout({
cartId,
regionId,
})
}If another storefront needs the same backend-facing rule, move that rule into libs/storefront-data so the next bugfix lands once.
Common Mistakes
HIGH Wrappers with no added behavior
Wrong:
export const useProducts = (params: ProductParams) =>
storefront.hooks.products.useProducts(params)Correct:
export const useProducts = storefront.hooks.products.useProductsA wrapper that adds no policy, translation, or side effect only obscures the preset surface.
Source: maintainer interview
HIGH Customer-specific read models inside the shared package
Wrong:
// libs/storefront-data/src/customers/types.ts
export type N1ProfileCard = {
loyaltyTier: string
}Correct:
// apps/n1/src/lib/customer-models.ts
export type N1ProfileCard = {
loyaltyTier: string
}The shared package owns reusable backend-facing contracts. A storefront-local read model belongs to the storefront until reuse is proven.
Source: libs/storefront-data/README.md, maintainer interview
HIGH Repeated backend behavior left inside one app
Wrong:
// apps/n1/src/lib/cart-hooks.ts
export function useSharedCartSemantics() {
return useMutation({ mutationFn: customCartMutation })
}Correct:
// libs/storefront-data/src/... shared seam consumed from the preset
export const storefront = createMedusaStorefrontPreset({ sdk })If checkout, cart, or product backend behavior is shared by most storefronts, it belongs in the library so bugfixes propagate once.
Source: maintainer interview, libs/storefront-data/AGENTS.md, libs/storefront-data/README.md
HIGH UI dependencies leaked into shared platform code
Wrong:
createMedusaStorefrontPreset({
sdk,
auth: {
hooks: {
onSuccess: () => toast.success("Logged in"),
},
},
})Correct:
function useLoginWithToast() {
return storefront.hooks.auth.useLogin({
onSuccess: () => toast.success("Logged in"),
})
}Storefront-specific UI dependencies belong in the app layer, even when the backend flow itself stays shared.
Source: libs/storefront-data/src/auth/hooks.ts, maintainer interview
See also: extend-storefront-data-for-new-backend-use-cases when the behavior is no longer truly storefront-specific.