UNPKG

@techsio/storefront-data

Version:

Medusa-first storefront data layer built on TanStack Query.

192 lines (141 loc) • 5.23 kB
--- name: extend-storefront-data-for-new-backend-use-cases description: > Load this skill when adding a new shared backend-facing capability to @techsio/storefront-data through preset wiring, normalized query keys, service-layer cancellation, and test-backed invariants. Use it when app code has proven a backend concern is no longer customer-specific and should become part of the shared storefront platform. type: core library: "@techsio/storefront-data" library_version: "0.1.0" requires: - decide-app-specific-overrides-vs-shared-platform sources: - "TechsioCZ/new-engine:libs/storefront-data/AGENTS.md" - "TechsioCZ/new-engine:libs/storefront-data/src/medusa/preset.ts" - "TechsioCZ/new-engine:libs/storefront-data/src/shared/query-keys.ts" - "TechsioCZ/new-engine:libs/storefront-data/src/orders/medusa-service.ts" - "TechsioCZ/new-engine:libs/storefront-data/tests/medusa.preset.test.tsx" - "TechsioCZ/new-engine:libs/storefront-data/tests/medusa.flow.test.tsx" --- This skill builds on `decide-app-specific-overrides-vs-shared-platform`. Use it only after deciding the behavior should become shared. # Extend storefront-data for new backend use cases ## Setup Treat new shared behavior as a coordinated change across types, query keys, service wiring, preset composition, and tests. ```ts // src/inventory/query-keys.ts import { createQueryKey, type QueryNamespace } from "../shared/query-keys" export const createInventoryQueryKeys = (namespace: QueryNamespace) => ({ all: () => createQueryKey(namespace, "inventory"), list: (input: { sku?: string }) => createQueryKey(namespace, "inventory", "list", input), }) ``` ```ts // src/inventory/medusa-service.ts import type { MedusaSdk } from "../shared/medusa-client" export function createMedusaInventoryService(sdk: MedusaSdk) { return { async getInventory(input: { sku?: string }, signal?: AbortSignal) { return sdk.client.fetch({ method: "GET", path: "/store/inventory", query: input, signal, }) }, } } ``` ## Core Patterns ### Wire new shared behavior through the preset ```ts // src/medusa/preset.ts const services = { ...existingServices, inventory: config.inventory?.service ?? createMedusaInventoryService(config.sdk), } ``` The preset is the canonical composition root. If the new domain never reaches the preset, it does not reach the intended integration path. ### Reuse normalized query-key helpers ```ts // src/inventory/hooks.ts const queryKeys = config.queryKeys ?? createInventoryQueryKeys(config.queryKeyNamespace ?? "storefront-data") ``` Treat `createQueryKey()` and the existing key factories as mandatory infrastructure, not optional style. ### Pin the new invariant with tests in `libs/storefront-data/tests` ```ts // tests/inventory.smoke.test.ts import { describe, expect, it } from "vitest" describe("inventory preset wiring", () => { it("exposes the inventory surface from the preset", () => { expect(true).toBe(true) }) }) ``` Use the test file as the place where the new shared contract becomes explicit. ## Common Mistakes ### HIGH New domain outside the preset Wrong: ```ts export const useInventory = createInventoryHooks({ service, queryKeyNamespace: "shop", }) ``` Correct: ```ts export const storefront = createMedusaStorefrontPreset({ sdk, inventory: { service, }, }) ``` The preset is the main composition root. A new capability that lives outside it immediately diverges from the intended integration path. Source: `libs/storefront-data/src/medusa/preset.ts`, `libs/storefront-data/AGENTS.md`, `libs/storefront-data/README.md` ### CRITICAL Hardcoded query keys Wrong: ```ts const queryKey = ["storefront", "inventory", params] ``` Correct: ```ts const queryKey = createQueryKey(namespace, "inventory", "list", params) ``` Shared domains must participate in normalized key generation or they drift from invalidation and cache-matching behavior everywhere else. Source: `libs/storefront-data/src/shared/query-keys.ts`, `libs/storefront-data/AGENTS.md` ### HIGH `AbortSignal` accepted but ignored Wrong: ```ts async function getInventory(params: InventoryInput, signal?: AbortSignal) { return sdk.store.product.list(params) } ``` Correct: ```ts async function getInventory(params: InventoryInput, signal?: AbortSignal) { return sdk.client.fetch({ method: "GET", path: "/store/inventory", query: params, signal, }) } ``` TanStack Query passes cancellation signals into query functions. Dropping them creates fake cancellation support. Source: `libs/storefront-data/src/orders/medusa-service.ts`, TanStack Query query cancellation docs ### HIGH Shared behavior without shared tests Wrong: ```ts // add new flow logic with no regression coverage ``` Correct: ```ts // add the new logic and pin the invariant in libs/storefront-data/tests/* ``` The library now treats tests as part of the consumer-facing contract. Missing regression coverage pushes risk back into each storefront. Source: `libs/storefront-data/tests/medusa.preset.test.tsx`, `libs/storefront-data/tests/medusa.flow.test.tsx` ## References - [Extension recipe](references/extension-recipe.md)