UNPKG

@techsio/storefront-data

Version:

Medusa-first storefront data layer built on TanStack Query.

225 lines (172 loc) • 6.14 kB
--- name: implement-ssr-prefetch-and-query-client-boundaries description: > Load this skill when using @techsio/storefront-data in Next.js Server Components with getServerQueryClient, HydrationBoundary, dehydrate, and preset-owned getListQueryOptions or getDetailQueryOptions. Use it for SSR prefetch, request-scoped query-client ownership, and avoiding query-key drift between server prefetch and client hooks. type: framework library: "@techsio/storefront-data" framework: react library_version: "0.1.0" requires: - setup-storefront-platform-in-next-app sources: - "TechsioCZ/new-engine:libs/storefront-data/README.md" - "TechsioCZ/new-engine:libs/storefront-data/src/server/get-query-client.ts" - "TechsioCZ/new-engine:libs/storefront-data/src/shared/query-client.ts" - "TechsioCZ/new-engine:libs/storefront-data/src/shared/query-keys.ts" - "TechsioCZ/new-engine:libs/storefront-data/src/products/hooks.ts" - "TechsioCZ/new-engine:libs/storefront-data/tests/ssr-hydration.smoke.test.tsx" --- This skill builds on `setup-storefront-platform-in-next-app`. Read it first for the preset and provider boundary. # Implement SSR prefetch and query-client boundaries ## Setup Use the request-scoped server helper inside Server Components and prefetch through query options that come from the preset. ```tsx // app/products/page.tsx import { dehydrate, HydrationBoundary } from "@tanstack/react-query" import { getServerQueryClient } from "@techsio/storefront-data/server/get-query-client" import { storefront } from "@/src/lib/storefront" import { ProductsPage } from "./products-page" export default async function Page() { const queryClient = getServerQueryClient() await queryClient.prefetchQuery( storefront.hooks.products.getListQueryOptions({ region_id: "reg_123", country_code: "cz", limit: 24, }) ) return ( <HydrationBoundary state={dehydrate(queryClient)}> <ProductsPage /> </HydrationBoundary> ) } ``` ## Hooks and Components ### Prefetch detail routes with the same query options the client hook will use ```tsx // app/products/[handle]/page.tsx import { dehydrate, HydrationBoundary } from "@tanstack/react-query" import { getServerQueryClient } from "@techsio/storefront-data/server/get-query-client" import { storefront } from "@/src/lib/storefront" import { ProductPage } from "./product-page" export default async function Page({ params, }: { params: Promise<{ handle: string }> }) { const { handle } = await params const queryClient = getServerQueryClient() await queryClient.prefetchQuery( storefront.hooks.products.getDetailQueryOptions({ handle, region_id: "reg_123", country_code: "cz", }) ) return ( <HydrationBoundary state={dehydrate(queryClient)}> <ProductPage handle={handle} /> </HydrationBoundary> ) } ``` ### Use `makeQueryClient` when code runs on the server outside RSC render ```ts // src/lib/server-sitemap.ts import { makeQueryClient } from "@techsio/storefront-data/shared/query-client" import { storefront } from "@/src/lib/storefront" export async function loadSitemapProducts() { const queryClient = makeQueryClient() await queryClient.prefetchQuery( storefront.hooks.products.getListQueryOptions({ region_id: "reg_123", country_code: "cz", limit: 100, }) ) return queryClient.getQueryData( storefront.queryKeys.products.list({ region_id: "reg_123", country_code: "cz", limit: 100, }) ) } ``` `getServerQueryClient()` is for Server Component render. `makeQueryClient()` is the safe explicit choice for standalone server utilities. ## Common Mistakes ### HIGH Raw server QueryClient instances Wrong: ```ts import { QueryClient } from "@tanstack/react-query" const queryClient = new QueryClient() await queryClient.prefetchQuery({ queryKey, queryFn }) ``` Correct: ```ts import { getServerQueryClient } from "@techsio/storefront-data/server/get-query-client" const queryClient = getServerQueryClient() await queryClient.prefetchQuery(options) ``` A raw QueryClient bypasses the library's documented request-scoped SSR path and makes hydration behavior drift from the preset setup. Source: `libs/storefront-data/src/server/get-query-client.ts`, `libs/storefront-data/AGENTS.md` ### HIGH Assuming request memoization outside RSC render Wrong: ```ts export async function GET() { const first = getServerQueryClient() const second = getServerQueryClient() } ``` Correct: ```ts export default async function Page() { const queryClient = getServerQueryClient() return <HydrationBoundary state={dehydrate(queryClient)} /> } ``` React `cache()` only gives request-scoped reuse during Server Component render. Outside that context, each call creates a fresh client. Source: `libs/storefront-data/src/server/get-query-client.ts` ### CRITICAL Hand-written query keys for SSR prefetch Wrong: ```ts await queryClient.prefetchQuery({ queryKey: ["shop", "products", params], queryFn: fetchProducts, }) ``` Correct: ```ts await queryClient.prefetchQuery( storefront.hooks.products.getListQueryOptions(params) ) ``` Manual keys drift from the normalized key builders and silently miss the hydrated client cache. Source: `libs/storefront-data/src/shared/query-keys.ts`, `libs/storefront-data/tests/ssr-hydration.smoke.test.tsx` ### HIGH Missing region-sensitive inputs in prefetch Wrong: ```ts await queryClient.prefetchQuery( storefront.hooks.products.getListQueryOptions({ limit: 24, }) ) ``` Correct: ```ts await queryClient.prefetchQuery( storefront.hooks.products.getListQueryOptions({ region_id: "reg_123", country_code: "cz", limit: 24, }) ) ``` SSR helpers do not know the current region implicitly. If server and client shape the inputs differently, you lose cache identity and can fetch different payloads. Source: `libs/storefront-data/src/products/hooks.ts`, `libs/storefront-data/src/products/medusa-service.ts` See also: `configure-pagination-prefetch-and-cache-policy` for skip modes, page planning, and normalized query inputs.